aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2017-08-13 16:21:10 -0400
committerGitHub <noreply@github.com>2017-08-13 16:21:10 -0400
commitdc578f3742b474bd85d556299a6b2763f4d9acda (patch)
treecc4a8d1de140ece77160349e51a64857656ab373
parentf6ed934a7e32bf10c3a141773d713bf3b19e784f (diff)
parent7f200f057d33e3ef52b1b1b1bf1767577295317e (diff)
Merge pull request #2815 from MediaBrowser/beta
Beta
-rw-r--r--Emby.Common.Implementations/Emby.Common.Implementations.csproj3
-rw-r--r--Emby.Common.Implementations/Logging/NlogManager.cs14
-rw-r--r--Emby.Common.Implementations/Net/UdpSocket.cs4
-rw-r--r--Emby.Common.Implementations/Networking/NetworkManager.cs2
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs6
-rw-r--r--Emby.Common.Implementations/packages.config4
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs8
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs455
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs36
-rw-r--r--Emby.Dlna/DlnaManager.cs10
-rw-r--r--Emby.Dlna/PlayTo/Device.cs45
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs2
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs3
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs2
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs28
-rw-r--r--Emby.Drawing/ImageProcessor.cs34
-rw-r--r--Emby.Photos/PhotoProvider.cs2
-rw-r--r--Emby.Server.Core/Emby.Server.Core.csproj176
-rw-r--r--Emby.Server.Core/Properties/AssemblyInfo.cs34
-rw-r--r--Emby.Server.Core/app.config11
-rw-r--r--Emby.Server.Core/packages.config6
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs3
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs9
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs (renamed from Emby.Server.Core/ApplicationHost.cs)268
-rw-r--r--Emby.Server.Implementations/ApplicationPathHelper.cs (renamed from Emby.Server.Core/ApplicationPathHelper.cs)2
-rw-r--r--Emby.Server.Implementations/Channels/ChannelImageProvider.cs8
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs41
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs6
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs9
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs5
-rw-r--r--Emby.Server.Implementations/Cryptography/ASN1.cs (renamed from Emby.Server.Core/Cryptography/ASN1.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/ASN1Convert.cs (renamed from Emby.Server.Core/Cryptography/ASN1Convert.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/BitConverterLE.cs (renamed from Emby.Server.Core/Cryptography/BitConverterLE.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/CertificateGenerator.cs (renamed from Emby.Server.Core/Cryptography/CertificateGenerator.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptoConvert.cs (renamed from Emby.Server.Core/Cryptography/CryptoConvert.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/PKCS1.cs (renamed from Emby.Server.Core/Cryptography/PKCS1.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/PKCS12.cs (renamed from Emby.Server.Core/Cryptography/PKCS12.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/PKCS7.cs (renamed from Emby.Server.Core/Cryptography/PKCS7.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/PKCS8.cs (renamed from Emby.Server.Core/Cryptography/PKCS8.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/PfxGenerator.cs (renamed from Emby.Server.Core/Cryptography/PfxGenerator.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X501Name.cs (renamed from Emby.Server.Core/Cryptography/X501Name.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509Builder.cs (renamed from Emby.Server.Core/Cryptography/X509Builder.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509Certificate.cs (renamed from Emby.Server.Core/Cryptography/X509Certificate.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509CertificateBuilder.cs (renamed from Emby.Server.Core/Cryptography/X509CertificateBuilder.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509CertificateCollection.cs (renamed from Emby.Server.Core/Cryptography/X509CertificateCollection.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509Extension.cs (renamed from Emby.Server.Core/Cryptography/X509Extension.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X509Extensions.cs (renamed from Emby.Server.Core/Cryptography/X509Extensions.cs)0
-rw-r--r--Emby.Server.Implementations/Cryptography/X520Attributes.cs (renamed from Emby.Server.Core/Cryptography/X520Attributes.cs)0
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs27
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs47
-rw-r--r--Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs284
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs535
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs95
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj98
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs (renamed from Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs)7
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs3
-rw-r--r--Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs813
-rw-r--r--Emby.Server.Implementations/FileOrganization/Extensions.cs33
-rw-r--r--Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs80
-rw-r--r--Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs283
-rw-r--r--Emby.Server.Implementations/FileOrganization/NameUtils.cs81
-rw-r--r--Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs101
-rw-r--r--Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs236
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs17
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs188
-rw-r--r--Emby.Server.Implementations/HttpServer/LoggerUtils.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs3
-rw-r--r--Emby.Server.Implementations/HttpServerFactory.cs (renamed from Emby.Server.Core/HttpServerFactory.cs)4
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs92
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs (renamed from Emby.Server.Core/IO/LibraryMonitor.cs)3
-rw-r--r--Emby.Server.Implementations/IO/MemoryStreamProvider.cs (renamed from Emby.Server.Core/IO/MemoryStreamProvider.cs)2
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs38
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs50
-rw-r--r--Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs7
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs15
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs22
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs20
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs20
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs9
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs5
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs2
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs42
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs17
-rw-r--r--Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs79
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs20
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs18
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json2
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs6
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/es.txt1
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ro.txt1
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/us.txt1
-rw-r--r--Emby.Server.Implementations/Localization/TextLocalizer.cs (renamed from Emby.Server.Core/Localization/TextLocalizer.cs)3
-rw-r--r--Emby.Server.Implementations/Logging/ConsoleLogger.cs (renamed from Emby.Server.Core/Logging/ConsoleLogger.cs)5
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs31
-rw-r--r--Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs13
-rw-r--r--Emby.Server.Implementations/Notifications/WebSocketNotifier.cs3
-rw-r--r--Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs6
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs17
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs23
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs14
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs9
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs8
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs3
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs11
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs11
-rw-r--r--Emby.Server.Implementations/Social/SharingRepository.cs3
-rw-r--r--Emby.Server.Implementations/Sorting/AirTimeComparer.cs71
-rw-r--r--Emby.Server.Implementations/SystemEvents.cs (renamed from Emby.Server.Core/SystemEvents.cs)2
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs51
-rw-r--r--Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs24
-rw-r--r--Emby.Server.Implementations/UserViews/DynamicImageProvider.cs24
-rw-r--r--Emby.Server.Implementations/packages.config5
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs746
-rw-r--r--MediaBrowser.Api/FilterService.cs4
-rw-r--r--MediaBrowser.Api/GamesService.cs7
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs23
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs28
-rw-r--r--MediaBrowser.Api/Library/FileOrganizationService.cs213
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs20
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs13
-rw-r--r--MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs (renamed from MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs)107
-rw-r--r--MediaBrowser.Api/LocalizationService.cs4
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj19
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs40
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs10
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs1024
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs331
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs970
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs163
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs137
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs591
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs67
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs429
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs100
-rw-r--r--MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs47
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs63
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs259
-rw-r--r--MediaBrowser.Api/Playback/TranscodingThrottler.cs176
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs339
-rw-r--r--MediaBrowser.Api/PlaylistService.cs7
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportBuilder.cs6
-rw-r--r--MediaBrowser.Api/Reports/ReportsService.cs7
-rw-r--r--MediaBrowser.Api/SearchService.cs7
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs2
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs13
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs1
-rw-r--r--MediaBrowser.Api/SuggestionsService.cs3
-rw-r--r--MediaBrowser.Api/System/SystemService.cs5
-rw-r--r--MediaBrowser.Api/TestService.cs77
-rw-r--r--MediaBrowser.Api/TvShowsService.cs52
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs3
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs14
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs14
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs451
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs6
-rw-r--r--MediaBrowser.Api/VideosService.cs8
-rw-r--r--MediaBrowser.Controller/Chapters/IChapterManager.cs8
-rw-r--r--MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs1
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs8
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs4
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs4
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs16
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs28
-rw-r--r--MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs187
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Extensions.cs23
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs78
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs29
-rw-r--r--MediaBrowser.Controller/Entities/IHasId.cs9
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs264
-rw-r--r--MediaBrowser.Controller/Entities/IHasMediaSources.cs4
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs257
-rw-r--r--MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs3
-rw-r--r--MediaBrowser.Controller/Entities/IHasTrailers.cs6
-rw-r--r--MediaBrowser.Controller/Entities/IHasUserData.cs7
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs6
-rw-r--r--MediaBrowser.Controller/Entities/KeywordExtensions.cs21
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs25
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs33
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs12
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs21
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs99
-rw-r--r--MediaBrowser.Controller/Entities/TagExtensions.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs8
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs33
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs125
-rw-r--r--MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs107
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs17
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs15
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs8
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs10
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs15
-rw-r--r--MediaBrowser.Controller/Library/TVUtils.cs8
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs7
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs21
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs13
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj8
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs17
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs161
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs10
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs21
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs149
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs4
-rw-r--r--MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs45
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs12
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs11
-rw-r--r--MediaBrowser.Controller/Providers/AlbumInfo.cs4
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs55
-rw-r--r--MediaBrowser.Controller/Providers/EpisodeInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/IDirectoryService.cs8
-rw-r--r--MediaBrowser.Controller/Providers/IDynamicImageProvider.cs4
-rw-r--r--MediaBrowser.Controller/Providers/IImageEnhancer.cs8
-rw-r--r--MediaBrowser.Controller/Providers/IImageProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs12
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteImageProvider.cs4
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemLookupInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/SongInfo.cs4
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs15
-rw-r--r--MediaBrowser.Controller/Sync/ISyncRepository.cs80
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs58
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs36
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj1
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs83
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs31
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs2
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs201
-rw-r--r--MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs58
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs62
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs375
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs219
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs197
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs305
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs70
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs182
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs1120
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs66
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj100
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets6
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs117
-rw-r--r--MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs341
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs1372
-rw-r--r--MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs33
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs120
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs29
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs17
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs20
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs28
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs349
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs90
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs39
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs394
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs738
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs60
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs44
-rw-r--r--MediaBrowser.MediaEncoding/packages.config3
-rw-r--r--MediaBrowser.Model/ApiClient/ApiClientExtensions.cs48
-rw-r--r--MediaBrowser.Model/ApiClient/ApiHelpers.cs22
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionMode.cs9
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionOptions.cs29
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionResult.cs21
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionState.cs13
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs1427
-rw-r--r--MediaBrowser.Model/ApiClient/IClientWebSocket.cs54
-rw-r--r--MediaBrowser.Model/ApiClient/IConnectionManager.cs192
-rw-r--r--MediaBrowser.Model/ApiClient/IDevice.cs44
-rw-r--r--MediaBrowser.Model/ApiClient/IServerEvents.cs152
-rw-r--r--MediaBrowser.Model/ApiClient/NetworkStatus.cs30
-rw-r--r--MediaBrowser.Model/ApiClient/RemoteLogoutReason.cs9
-rw-r--r--MediaBrowser.Model/ApiClient/ServerCredentials.cs131
-rw-r--r--MediaBrowser.Model/ApiClient/ServerInfo.cs127
-rw-r--r--MediaBrowser.Model/ApiClient/ServerUserInfo.cs9
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs6
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs8
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Dlna/CodecProfile.cs10
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs39
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs31
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs21
-rw-r--r--MediaBrowser.Model/Dlna/ResponseProfile.cs7
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs93
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs4
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs114
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs4
-rw-r--r--MediaBrowser.Model/Dto/StudioDto.cs29
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs4
-rw-r--r--MediaBrowser.Model/Entities/MetadataFields.cs4
-rw-r--r--MediaBrowser.Model/Entities/VideoType.cs6
-rw-r--r--MediaBrowser.Model/Extensions/LinqExtensions.cs13
-rw-r--r--MediaBrowser.Model/FileOrganization/AutoOrganizeOptions.cs24
-rw-r--r--MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs26
-rw-r--r--MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs109
-rw-r--r--MediaBrowser.Model/FileOrganization/FileOrganizationResultQuery.cs18
-rw-r--r--MediaBrowser.Model/FileOrganization/FileOrganizerType.cs9
-rw-r--r--MediaBrowser.Model/FileOrganization/FileSortingStatus.cs9
-rw-r--r--MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs16
-rw-r--r--MediaBrowser.Model/FileOrganization/TvFileOrganizationOptions.cs40
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs4
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj27
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaInfo.cs6
-rw-r--r--MediaBrowser.Model/Net/ISocket.cs1
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs18
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs13
-rw-r--r--MediaBrowser.Model/Querying/ItemQuery.cs332
-rw-r--r--MediaBrowser.Model/Querying/SeasonQuery.cs22
-rw-r--r--MediaBrowser.Model/Search/SearchHint.cs6
-rw-r--r--MediaBrowser.Model/Services/IHttpRequest.cs1
-rw-r--r--MediaBrowser.Model/Services/IHttpResponse.cs1
-rw-r--r--MediaBrowser.Model/Services/IHttpResult.cs1
-rw-r--r--MediaBrowser.Model/Services/QueryParamCollection.cs3
-rw-r--r--MediaBrowser.Model/Social/ISharingRepository.cs6
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs3
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItem.cs5
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItemStatus.cs4
-rw-r--r--MediaBrowser.Model/Sync/SyncJobStatus.cs3
-rw-r--r--MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs12
-rw-r--r--MediaBrowser.Model/Tasks/TaskInfo.cs4
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs2
-rw-r--r--MediaBrowser.Providers/Books/AudioBookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Books/BookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs9
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs12
-rw-r--r--MediaBrowser.Providers/Channels/ChannelMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Chapters/ChapterManager.cs4
-rw-r--r--MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Folders/FolderMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/UserViewMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Games/GameMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Games/GameSystemMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Genres/GenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/ImagesByName/ImageUtils.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/AudioRecordingService.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/VideoRecordingService.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs29
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs38
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs19
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs28
-rw-r--r--MediaBrowser.Providers/Manager/ProviderUtils.cs43
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs13
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs12
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs5
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs101
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs3
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs146
-rw-r--r--MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs19
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs12
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs3
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs10
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs3
-rw-r--r--MediaBrowser.Providers/Music/FanArtAlbumProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/FanArtArtistProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs3
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonProvider.cs2
-rw-r--r--MediaBrowser.Providers/People/PersonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/People/TvdbPersonImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Studios/StudioMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs10
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs3
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs4
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs12
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs5
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs7
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs41
-rw-r--r--MediaBrowser.Providers/TV/TvExternalIds.cs23
-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.Server.Mac/Emby.Server.Mac.csproj6
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj17
-rw-r--r--MediaBrowser.Server.Mono/Program.cs4
-rw-r--r--MediaBrowser.Server.Mono/packages.config6
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs7
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj17
-rw-r--r--MediaBrowser.ServerApplication/packages.config6
-rw-r--r--MediaBrowser.Tests/MediaBrowser.Tests.csproj29
-rw-r--r--MediaBrowser.Tests/MediaEncoding/Subtitles/AssParserTests.cs1
-rw-r--r--MediaBrowser.Tests/MediaEncoding/Subtitles/SrtParserTests.cs2
-rw-r--r--MediaBrowser.Tests/MediaEncoding/Subtitles/VttWriterTest.cs2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs15
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs5
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs43
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs9
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs22
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs32
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs18
-rw-r--r--MediaBrowser.sln79
-rw-r--r--Mono.Nat/NatUtility.cs6
-rw-r--r--Mono.Nat/Pmp/PmpNatDevice.cs3
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec24
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
-rw-r--r--SocketHttpListener/Net/WebHeaderCollection.cs3
452 files changed, 3187 insertions, 22312 deletions
diff --git a/Emby.Common.Implementations/Emby.Common.Implementations.csproj b/Emby.Common.Implementations/Emby.Common.Implementations.csproj
index 00c90d16e..cbd077e19 100644
--- a/Emby.Common.Implementations/Emby.Common.Implementations.csproj
+++ b/Emby.Common.Implementations/Emby.Common.Implementations.csproj
@@ -32,8 +32,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
diff --git a/Emby.Common.Implementations/Logging/NlogManager.cs b/Emby.Common.Implementations/Logging/NlogManager.cs
index f7b723e8b..4446e2cdb 100644
--- a/Emby.Common.Implementations/Logging/NlogManager.cs
+++ b/Emby.Common.Implementations/Logging/NlogManager.cs
@@ -152,13 +152,23 @@ namespace Emby.Common.Implementations.Logging
RemoveTarget("ApplicationLogFileWrapper");
- var wrapper = new AsyncTargetWrapper();
+ // https://github.com/NLog/NLog/wiki/Performance
+ var wrapper = new AsyncTargetWrapper
+ {
+ OverflowAction = AsyncTargetWrapperOverflowAction.Block,
+ QueueLimit = 10000,
+ BatchSize = 500,
+ TimeToSleepBetweenBatches = 50
+ };
+
wrapper.Name = "ApplicationLogFileWrapper";
var logFile = new FileTarget
{
FileName = path,
- Layout = "${longdate} ${level} ${logger}: ${message}"
+ Layout = "${longdate} ${level} ${logger}: ${message}",
+ KeepFileOpen = true,
+ ConcurrentWrites = false
};
logFile.Name = "ApplicationLogFile";
diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs
index df1099d3d..542d16d24 100644
--- a/Emby.Common.Implementations/Net/UdpSocket.cs
+++ b/Emby.Common.Implementations/Net/UdpSocket.cs
@@ -37,8 +37,6 @@ namespace Emby.Common.Implementations.Net
private TaskCompletionSource<SocketReceiveResult> _currentReceiveTaskCompletionSource;
private TaskCompletionSource<int> _currentSendTaskCompletionSource;
- private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
-
public UdpSocket(Socket socket, int localPort, IPAddress ip)
{
if (socket == null) throw new ArgumentNullException("socket");
@@ -234,8 +232,6 @@ namespace Emby.Common.Implementations.Net
if (socket != null)
socket.Dispose();
- _sendLock.Dispose();
-
var tcs = _currentReceiveTaskCompletionSource;
if (tcs != null)
{
diff --git a/Emby.Common.Implementations/Networking/NetworkManager.cs b/Emby.Common.Implementations/Networking/NetworkManager.cs
index 2f218656c..354107bb7 100644
--- a/Emby.Common.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Common.Implementations/Networking/NetworkManager.cs
@@ -506,7 +506,7 @@ namespace Emby.Common.Implementations.Networking
public async Task<IpAddressInfo[]> GetHostAddressesAsync(string host)
{
var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
- return addresses.Select(ToIpAddressInfo).ToArray();
+ return addresses.Select(ToIpAddressInfo).ToArray(addresses.Length);
}
/// <summary>
diff --git a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index c373ffddb..dd840677a 100644
--- a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Extensions;
namespace Emby.Common.Implementations.ScheduledTasks
{
@@ -274,7 +275,8 @@ namespace Emby.Common.Implementations.ScheduledTasks
{
get
{
- return InternalTriggers.Select(i => i.Item1).ToArray();
+ var triggers = InternalTriggers;
+ return triggers.Select(i => i.Item1).ToArray(triggers.Length);
}
set
{
@@ -288,7 +290,7 @@ namespace Emby.Common.Implementations.ScheduledTasks
SaveTriggers(triggerList);
- InternalTriggers = triggerList.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
+ InternalTriggers = triggerList.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray(triggerList.Length);
}
}
diff --git a/Emby.Common.Implementations/packages.config b/Emby.Common.Implementations/packages.config
index a255465cc..eb8fd586e 100644
--- a/Emby.Common.Implementations/packages.config
+++ b/Emby.Common.Implementations/packages.config
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="NLog" version="4.4.11" targetFramework="net46" />
- <package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
+ <package id="NLog" version="4.4.12" targetFramework="net46" />
+ <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
<package id="SharpCompress" version="0.14.0" targetFramework="net462" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 4a36a16eb..92d388e3b 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
@@ -31,6 +32,7 @@ namespace Emby.Dlna.ContentDirectory
private readonly IUserViewManager _userViewManager;
private readonly Func<IMediaEncoder> _mediaEncoder;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
+ private readonly ITVSeriesManager _tvSeriesManager;
public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager,
@@ -39,7 +41,7 @@ namespace Emby.Dlna.ContentDirectory
IServerConfigurationManager config,
IUserManager userManager,
ILogger logger,
- IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
+ IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(logger, httpClient)
{
_dlna = dlna;
@@ -54,6 +56,7 @@ namespace Emby.Dlna.ContentDirectory
_userViewManager = userViewManager;
_mediaEncoder = mediaEncoder;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
+ _tvSeriesManager = tvSeriesManager;
}
private int SystemUpdateId
@@ -97,7 +100,8 @@ namespace Emby.Dlna.ContentDirectory
_mediaSourceManager,
_userViewManager,
_mediaEncoder(),
- XmlReaderSettingsFactory)
+ XmlReaderSettingsFactory,
+ _tvSeriesManager)
.ProcessControlRequest(request);
}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 9345a1df7..96b282d04 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -27,8 +27,10 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.ContentDirectory
{
@@ -40,6 +42,7 @@ namespace Emby.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config;
private readonly User _user;
private readonly IUserViewManager _userViewManager;
+ private readonly ITVSeriesManager _tvSeriesManager;
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@@ -53,7 +56,7 @@ namespace Emby.Dlna.ContentDirectory
private readonly DeviceProfile _profile;
- public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
+ public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(config, logger, xmlReaderSettingsFactory)
{
_libraryManager = libraryManager;
@@ -62,6 +65,7 @@ namespace Emby.Dlna.ContentDirectory
_systemUpdateId = systemUpdateId;
_channelManager = channelManager;
_userViewManager = userViewManager;
+ _tvSeriesManager = tvSeriesManager;
_profile = profile;
_config = config;
@@ -454,14 +458,14 @@ namespace Emby.Dlna.ContentDirectory
{
Limit = limit,
StartIndex = startIndex,
- SortBy = sortOrders.ToArray(),
+ SortBy = sortOrders.ToArray(sortOrders.Count),
SortOrder = sort.SortOrder,
User = user,
Recursive = true,
IsMissing = false,
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsFolder = isFolder,
- MediaTypes = mediaTypes.ToArray(),
+ MediaTypes = mediaTypes.ToArray(mediaTypes.Count),
DtoOptions = GetDtoOptions()
});
}
@@ -488,6 +492,14 @@ namespace Emby.Dlna.ContentDirectory
{
return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
}
+ if (collectionFolder != null && string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
+ }
+ if (collectionFolder != null && string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetTvFolders(item, user, stubType, sort, startIndex, limit);
+ }
if (stubType.HasValue)
{
@@ -497,12 +509,12 @@ namespace Emby.Dlna.ContentDirectory
{
ItemId = item.Id
- }).ToArray();
+ });
var result = new QueryResult<ServerItem>
{
- Items = items.Select(i => new ServerItem(i)).ToArray(),
- TotalRecordCount = items.Length
+ Items = items.Select(i => new ServerItem(i)).ToArray(items.Count),
+ TotalRecordCount = items.Count
};
return ApplyPaging(result, startIndex, limit);
@@ -524,8 +536,8 @@ namespace Emby.Dlna.ContentDirectory
Limit = limit,
StartIndex = startIndex,
User = user,
- IsMissing = false,
- PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows },
+ IsVirtualItem = false,
+ PresetViews = new string[] { },
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false,
DtoOptions = GetDtoOptions()
@@ -651,11 +663,236 @@ namespace Emby.Dlna.ContentDirectory
return new QueryResult<ServerItem>
{
- Items = list.ToArray(),
+ Items = list.ToArray(list.Count),
+ TotalRecordCount = list.Count
+ };
+ }
+
+ private QueryResult<ServerItem> GetMovieFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
+ {
+ var query = new InternalItemsQuery(user)
+ {
+ StartIndex = startIndex,
+ Limit = limit
+ };
+ SetSorting(query, sort, false);
+
+ if (stubType.HasValue && stubType.Value == StubType.ContinueWatching)
+ {
+ return GetMovieContinueWatching(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Latest)
+ {
+ return GetMovieLatest(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Movies)
+ {
+ return GetMovieMovies(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Collections)
+ {
+ return GetMovieCollections(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Favorites)
+ {
+ return GetMovieFavorites(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Genres)
+ {
+ return GetGenres(item, user, query);
+ }
+
+ var list = new List<ServerItem>();
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.ContinueWatching
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Latest
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Movies
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Collections
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Favorites
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Genres
+ });
+
+ return new QueryResult<ServerItem>
+ {
+ Items = list.ToArray(list.Count),
+ TotalRecordCount = list.Count
+ };
+ }
+
+ private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
+ {
+ var query = new InternalItemsQuery(user)
+ {
+ StartIndex = startIndex,
+ Limit = limit
+ };
+ SetSorting(query, sort, false);
+
+ if (stubType.HasValue && stubType.Value == StubType.ContinueWatching)
+ {
+ return GetMovieContinueWatching(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.NextUp)
+ {
+ return GetNextUp(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Latest)
+ {
+ return GetTvLatest(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Series)
+ {
+ return GetSeries(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.FavoriteSeries)
+ {
+ return GetFavoriteSeries(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.FavoriteEpisodes)
+ {
+ return GetFavoriteEpisodes(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Genres)
+ {
+ return GetGenres(item, user, query);
+ }
+
+ var list = new List<ServerItem>();
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.ContinueWatching
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.NextUp
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Latest
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Series
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.FavoriteSeries
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.FavoriteEpisodes
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Genres
+ });
+
+ return new QueryResult<ServerItem>
+ {
+ Items = list.ToArray(list.Count),
TotalRecordCount = list.Count
};
}
+ private QueryResult<ServerItem> GetMovieContinueWatching(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+
+ query.OrderBy = new List<Tuple<string, SortOrder>>
+ {
+ new Tuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
+ new Tuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
+ };
+
+ query.IsResumable = true;
+ query.Limit = 10;
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetSeries(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+
+ query.IncludeItemTypes = new[] { typeof(Series).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMovieMovies(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+
+ query.IncludeItemTypes = new[] { typeof(Movie).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMovieCollections(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ //query.Parent = parent;
+ query.SetUser(user);
+
+ query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
private QueryResult<ServerItem> GetMusicAlbums(BaseItem parent, User user, InternalItemsQuery query)
{
query.Recursive = true;
@@ -695,6 +932,45 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
+ private QueryResult<ServerItem> GetFavoriteSeries(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+ query.IsFavorite = true;
+ query.IncludeItemTypes = new[] { typeof(Series).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetFavoriteEpisodes(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+ query.IsFavorite = true;
+ query.IncludeItemTypes = new[] { typeof(Episode).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMovieFavorites(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+ query.IsFavorite = true;
+ query.IncludeItemTypes = new[] { typeof(Movie).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
private QueryResult<ServerItem> GetFavoriteAlbums(BaseItem parent, User user, InternalItemsQuery query)
{
query.Recursive = true;
@@ -708,6 +984,24 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
+ private QueryResult<ServerItem> GetGenres(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user)
+ {
+ AncestorIds = new[] { parent.Id.ToString("N") },
+ StartIndex = query.StartIndex,
+ Limit = query.Limit
+ });
+
+ var result = new QueryResult<BaseItem>
+ {
+ TotalRecordCount = genresResult.TotalRecordCount,
+ Items = genresResult.Items.Select(i => i.Item1).ToArray(genresResult.Items.Length)
+ };
+
+ return ToResult(result);
+ }
+
private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, User user, InternalItemsQuery query)
{
var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
@@ -720,7 +1014,7 @@ namespace Emby.Dlna.ContentDirectory
var result = new QueryResult<BaseItem>
{
TotalRecordCount = genresResult.TotalRecordCount,
- Items = genresResult.Items.Select(i => i.Item1).ToArray()
+ Items = genresResult.Items.Select(i => i.Item1).ToArray(genresResult.Items.Length)
};
return ToResult(result);
@@ -738,7 +1032,7 @@ namespace Emby.Dlna.ContentDirectory
var result = new QueryResult<BaseItem>
{
TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
+ Items = artists.Items.Select(i => i.Item1).ToArray(artists.Items.Length)
};
return ToResult(result);
@@ -756,7 +1050,7 @@ namespace Emby.Dlna.ContentDirectory
var result = new QueryResult<BaseItem>
{
TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
+ Items = artists.Items.Select(i => i.Item1).ToArray(artists.Items.Length)
};
return ToResult(result);
@@ -775,7 +1069,7 @@ namespace Emby.Dlna.ContentDirectory
var result = new QueryResult<BaseItem>
{
TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
+ Items = artists.Items.Select(i => i.Item1).ToArray(artists.Items.Length)
};
return ToResult(result);
@@ -810,6 +1104,55 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(items);
}
+ private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.SortBy = new string[] { };
+
+ var result = _tvSeriesManager.GetNextUp(new NextUpQuery
+ {
+ Limit = query.Limit,
+ StartIndex = query.StartIndex,
+ UserId = query.User.Id.ToString("N")
+
+ }, new List<Folder> { (Folder)parent }, query.DtoOptions);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.SortBy = new string[] { };
+
+ var items = _userViewManager.GetLatestItems(new LatestItemsQuery
+ {
+ UserId = user.Id.ToString("N"),
+ Limit = 50,
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ ParentId = parent == null ? null : parent.Id.ToString("N"),
+ GroupItems = false
+
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+
+ return ToResult(items);
+ }
+
+ private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.SortBy = new string[] { };
+
+ var items = _userViewManager.GetLatestItems(new LatestItemsQuery
+ {
+ UserId = user.Id.ToString("N"),
+ Limit = 50,
+ IncludeItemTypes = new[] { typeof(Movie).Name },
+ ParentId = parent == null ? null : parent.Id.ToString("N"),
+ GroupItems = true
+
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+
+ return ToResult(items);
+ }
+
private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
@@ -854,7 +1197,7 @@ namespace Emby.Dlna.ContentDirectory
{
var serverItems = result
.Select(i => new ServerItem(i))
- .ToArray();
+ .ToArray(result.Count);
return new QueryResult<ServerItem>
{
@@ -868,7 +1211,7 @@ namespace Emby.Dlna.ContentDirectory
var serverItems = result
.Items
.Select(i => new ServerItem(i))
- .ToArray();
+ .ToArray(result.Items.Length);
return new QueryResult<ServerItem>
{
@@ -885,7 +1228,7 @@ namespace Emby.Dlna.ContentDirectory
sortOrders.Add(ItemSortBy.SortName);
}
- query.SortBy = sortOrders.ToArray();
+ query.SortBy = sortOrders.ToArray(sortOrders.Count);
query.SortOrder = sort.SortOrder;
}
@@ -901,8 +1244,7 @@ namespace Emby.Dlna.ContentDirectory
DtoOptions = GetDtoOptions()
});
- var serverItems = itemsResult.Items.Select(i => new ServerItem(i))
- .ToArray();
+ var serverItems = itemsResult.Items.Select(i => new ServerItem(i)).ToArray(itemsResult.Items.Length);
return new QueryResult<ServerItem>
{
@@ -942,65 +1284,16 @@ namespace Emby.Dlna.ContentDirectory
id = parts[23];
}
- if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Folder;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.People;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("latest_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Latest;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("playlists_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Playlists;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("Albums_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Albums;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("AlbumArtists_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.AlbumArtists;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("Artists_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Artists;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("Genres_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.Genres;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("Songs_", StringComparison.OrdinalIgnoreCase))
+ var enumNames = Enum.GetNames(typeof(StubType));
+ foreach (var name in enumNames)
{
- stubType = StubType.Songs;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("FavoriteAlbums_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.FavoriteAlbums;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("FavoriteArtists_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.FavoriteArtists;
- id = id.Split(new[] { '_' }, 2)[1];
- }
- else if (id.StartsWith("FavoriteSongs_", StringComparison.OrdinalIgnoreCase))
- {
- stubType = StubType.FavoriteSongs;
- id = id.Split(new[] { '_' }, 2)[1];
+ if (id.StartsWith(name + "_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = (StubType)Enum.Parse(typeof(StubType), name, true);
+ id = id.Split(new[] { '_' }, 2)[1];
+
+ break;
+ }
}
if (Guid.TryParse(id, out itemId))
@@ -1048,6 +1341,14 @@ namespace Emby.Dlna.ContentDirectory
Genres = 8,
FavoriteSongs = 9,
FavoriteArtists = 10,
- FavoriteAlbums = 11
+ FavoriteAlbums = 11,
+ ContinueWatching = 12,
+ Movies = 13,
+ Collections = 14,
+ Favorites = 15,
+ NextUp = 16,
+ Series = 17,
+ FavoriteSeries = 18,
+ FavoriteEpisodes = 19
}
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index d2a160cf7..71a049394 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -193,7 +193,7 @@ namespace Emby.Dlna.Didl
{
if (streamInfo == null)
{
- var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList();
+ var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user);
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
{
@@ -439,6 +439,38 @@ namespace Emby.Dlna.Didl
{
return _localization.GetLocalizedString("ViewTypeMusicFavoriteSongs");
}
+ if (itemStubType.HasValue && itemStubType.Value == StubType.ContinueWatching)
+ {
+ return _localization.GetLocalizedString("ViewTypeMovieResume");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Movies)
+ {
+ return _localization.GetLocalizedString("ViewTypeMovieMovies");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Collections)
+ {
+ return _localization.GetLocalizedString("ViewTypeMovieCollections");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Favorites)
+ {
+ return _localization.GetLocalizedString("ViewTypeMovieFavorites");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.NextUp)
+ {
+ return _localization.GetLocalizedString("ViewTypeTvNextUp");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSeries)
+ {
+ return _localization.GetLocalizedString("ViewTypeTvFavoriteSeries");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteEpisodes)
+ {
+ return _localization.GetLocalizedString("ViewTypeTvFavoriteEpisodes");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Series)
+ {
+ return _localization.GetLocalizedString("ViewTypeTvShowSeries");
+ }
var episode = item as Episode;
var season = context as Season;
@@ -476,7 +508,7 @@ namespace Emby.Dlna.Didl
if (streamInfo == null)
{
- var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
+ var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user);
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
{
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 82975ce22..847f63619 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -18,6 +18,7 @@ using System.Text;
using System.Text.RegularExpressions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Reflection;
+using MediaBrowser.Model.Extensions;
namespace Emby.Dlna
{
@@ -106,7 +107,6 @@ namespace Emby.Dlna
}
else
{
- _logger.Debug("No matching device profile found. The default will need to be used.");
LogUnmatchedProfile(deviceInfo);
}
@@ -220,12 +220,8 @@ namespace Emby.Dlna
}
else
{
- var msg = new StringBuilder();
- foreach (var header in headers)
- {
- msg.AppendLine(header.Key + ": " + header.Value);
- }
- _logger.LogMultiline("No matching device profile found. The default will need to be used.", LogSeverity.Info, msg);
+ var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(headers.Count));
+ _logger.Debug("No matching device profile found. {0}", headerString);
}
return profile;
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index e22298010..213e7367f 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using System.Xml.Linq;
using Emby.Dlna.Server;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.PlayTo
{
@@ -112,7 +113,7 @@ namespace Emby.Dlna.PlayTo
private int GetInactiveTimerIntervalMs()
{
- return 30000;
+ return Timeout.Infinite;
}
public void Start()
@@ -160,18 +161,15 @@ namespace Emby.Dlna.PlayTo
if (_disposed)
return;
- if (!_timerActive)
+ lock (_timerLock)
{
- lock (_timerLock)
+ if (!_timerActive)
{
- if (!_timerActive)
- {
- _logger.Debug("RestartTimer");
- _timer.Change(10, GetPlaybackTimerIntervalMs());
- }
-
- _timerActive = true;
+ _logger.Debug("RestartTimer");
+ _timer.Change(10, GetPlaybackTimerIntervalMs());
}
+
+ _timerActive = true;
}
}
@@ -183,23 +181,20 @@ namespace Emby.Dlna.PlayTo
if (_disposed)
return;
- if (_timerActive)
+ lock (_timerLock)
{
- lock (_timerLock)
+ if (_timerActive)
{
- if (_timerActive)
- {
- _logger.Debug("RestartTimerInactive");
- var interval = GetInactiveTimerIntervalMs();
+ _logger.Debug("RestartTimerInactive");
+ var interval = GetInactiveTimerIntervalMs();
- if (_timer != null)
- {
- _timer.Change(interval, interval);
- }
+ if (_timer != null)
+ {
+ _timer.Change(interval, interval);
}
-
- _timerActive = false;
}
+
+ _timerActive = false;
}
}
@@ -492,6 +487,10 @@ namespace Emby.Dlna.PlayTo
RestartTimer();
}
}
+ else
+ {
+ RestartTimerInactive();
+ }
}
catch (HttpException ex)
{
@@ -890,7 +889,7 @@ namespace Emby.Dlna.PlayTo
if (room != null && !string.IsNullOrWhiteSpace(room.Value))
friendlyNames.Add(room.Value);
- deviceProperties.Name = string.Join(" ", friendlyNames.ToArray());
+ deviceProperties.Name = string.Join(" ", friendlyNames.ToArray(friendlyNames.Count));
var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
if (model != null)
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 15d73e824..7164cf598 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -503,7 +503,7 @@ namespace Emby.Dlna.PlayTo
var hasMediaSources = item as IHasMediaSources;
var mediaSources = hasMediaSources != null
- ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList()
+ ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user))
: new List<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index 06ce93640..46639ee42 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Linq;
using System.Xml.Serialization;
+using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Profiles
{
@@ -172,7 +173,7 @@ namespace Emby.Dlna.Profiles
Value = value
});
- XmlRootAttributes = list.ToArray();
+ XmlRootAttributes = list.ToArray(list.Count);
}
}
}
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index 2a4a5792f..bba4adc5f 100644
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -226,7 +226,7 @@ namespace Emby.Dlna.Server
}
}
- var characters = characterList.ToArray();
+ var characters = characterList.ToArray(characterList.Count);
var serverName = new string(characters);
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 3092589c1..7cd10bd01 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -11,6 +11,7 @@ using System.Xml;
using Emby.Dlna.Didl;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Service
{
@@ -235,26 +236,29 @@ namespace Emby.Dlna.Service
private void LogRequest(ControlRequest request)
{
- var builder = new StringBuilder();
+ if (!Config.GetDlnaConfiguration().EnableDebugLog)
+ {
+ return;
+ }
- var headers = string.Join(", ", request.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
- builder.AppendFormat("Headers: {0}", headers);
- builder.AppendLine();
- //builder.Append(request.InputXml);
+ var originalHeaders = request.Headers;
+ var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count));
- Logger.LogMultiline("Control request", LogSeverity.Debug, builder);
+ Logger.Debug("Control request. Headers: {0}", headers);
}
private void LogResponse(ControlResponse response)
{
- var builder = new StringBuilder();
+ if (!Config.GetDlnaConfiguration().EnableDebugLog)
+ {
+ return;
+ }
- var headers = string.Join(", ", response.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
- builder.AppendFormat("Headers: {0}", headers);
- builder.AppendLine();
- builder.Append(response.Xml);
+ var originalHeaders = response.Headers;
+ var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count));
+ //builder.Append(response.Xml);
- Logger.LogMultiline("Control response", LogSeverity.Debug, builder);
+ Logger.Debug("Control response. Headers: {0}", headers);
}
}
}
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index be4591223..bd23eba7a 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -17,12 +17,10 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using Emby.Drawing.Common;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
-using TagLib;
+using MediaBrowser.Model.Extensions;
namespace Emby.Drawing
{
@@ -171,6 +169,13 @@ namespace Emby.Drawing
return _imageEncoder.SupportedOutputFormats;
}
+ private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp" };
+ private bool SupportsTransparency(string path)
+ {
+ return TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
+ ;
+ }
+
public async Task<Tuple<string, string, DateTime>> ProcessImage(ImageProcessingOptions options)
{
if (options == null)
@@ -179,7 +184,7 @@ namespace Emby.Drawing
}
var originalImage = options.Image;
- IHasImages item = options.Item;
+ IHasMetadata item = options.Item;
if (!originalImage.IsLocalFile)
{
@@ -260,6 +265,11 @@ namespace Emby.Drawing
item = _libraryManager().GetItemById(options.ItemId);
}
+ if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath))
+ {
+ options.CropWhiteSpace = false;
+ }
+
var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, autoOrient, orientation, quality, options, outputFormat);
if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
@@ -594,7 +604,7 @@ namespace Emby.Drawing
/// <param name="image">The image.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public string GetImageCacheTag(IHasImages item, ItemImageInfo image)
+ public string GetImageCacheTag(IHasMetadata item, ItemImageInfo image)
{
if (item == null)
{
@@ -619,7 +629,7 @@ namespace Emby.Drawing
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public string GetImageCacheTag(IHasImages item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers)
+ public string GetImageCacheTag(IHasMetadata item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers)
{
if (item == null)
{
@@ -650,7 +660,7 @@ namespace Emby.Drawing
var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
cacheKeys.Add(originalImagePath + dateModified.Ticks);
- return string.Join("|", cacheKeys.ToArray()).GetMD5().ToString("N");
+ return string.Join("|", cacheKeys.ToArray(cacheKeys.Count)).GetMD5().ToString("N");
}
/// <summary>
@@ -660,7 +670,7 @@ namespace Emby.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{System.String}.</returns>
- public async Task<string> GetEnhancedImage(IHasImages item, ImageType imageType, int imageIndex)
+ public async Task<string> GetEnhancedImage(IHasMetadata item, ImageType imageType, int imageIndex)
{
var enhancers = GetSupportedEnhancers(item, imageType).ToList();
@@ -672,7 +682,7 @@ namespace Emby.Drawing
}
private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image,
- IHasImages item,
+ IHasMetadata item,
int imageIndex,
List<IImageEnhancer> enhancers)
{
@@ -717,7 +727,7 @@ namespace Emby.Drawing
/// item
/// </exception>
private async Task<string> GetEnhancedImageInternal(string originalImagePath,
- IHasImages item,
+ IHasMetadata item,
ImageType imageType,
int imageIndex,
IEnumerable<IImageEnhancer> supportedEnhancers,
@@ -771,7 +781,7 @@ namespace Emby.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{EnhancedImage}.</returns>
- private async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, IHasImages item, ImageType imageType, int imageIndex)
+ private async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, IHasMetadata item, ImageType imageType, int imageIndex)
{
// Run the enhancers sequentially in order of priority
foreach (var enhancer in imageEnhancers)
@@ -856,7 +866,7 @@ namespace Emby.Drawing
_logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
}
- public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType)
+ public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType)
{
return ImageEnhancers.Where(i =>
{
diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index 57047cf81..c3c30ab6d 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -111,7 +111,7 @@ namespace Emby.Photos
}
item.Genres = image.ImageTag.Genres.ToList();
- item.Tags = image.ImageTag.Keywords.ToList();
+ item.Tags = image.ImageTag.Keywords;
item.Software = image.ImageTag.Software;
if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
diff --git a/Emby.Server.Core/Emby.Server.Core.csproj b/Emby.Server.Core/Emby.Server.Core.csproj
deleted file mode 100644
index 063ef6eb9..000000000
--- a/Emby.Server.Core/Emby.Server.Core.csproj
+++ /dev/null
@@ -1,176 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Server.Core</RootNamespace>
- <AssemblyName>Emby.Server.Core</AssemblyName>
- <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
- <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Configuration" />
- <Reference Include="System.Core" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Net.Http" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="ApplicationHost.cs" />
- <Compile Include="ApplicationPathHelper.cs" />
- <Compile Include="Cryptography\ASN1.cs" />
- <Compile Include="Cryptography\ASN1Convert.cs" />
- <Compile Include="Cryptography\BitConverterLE.cs" />
- <Compile Include="Cryptography\CertificateGenerator.cs" />
- <Compile Include="Cryptography\CryptoConvert.cs" />
- <Compile Include="Cryptography\PfxGenerator.cs" />
- <Compile Include="Cryptography\PKCS1.cs" />
- <Compile Include="Cryptography\PKCS12.cs" />
- <Compile Include="Cryptography\PKCS7.cs" />
- <Compile Include="Cryptography\PKCS8.cs" />
- <Compile Include="Cryptography\X501Name.cs" />
- <Compile Include="Cryptography\X509Builder.cs" />
- <Compile Include="Cryptography\X509Certificate.cs" />
- <Compile Include="Cryptography\X509CertificateBuilder.cs" />
- <Compile Include="Cryptography\X509CertificateCollection.cs" />
- <Compile Include="Cryptography\X509Extension.cs" />
- <Compile Include="Cryptography\X509Extensions.cs" />
- <Compile Include="Cryptography\X520Attributes.cs" />
- <Compile Include="EntryPoints\ExternalPortForwarding.cs" />
- <Compile Include="HttpServerFactory.cs" />
- <Compile Include="IO\LibraryMonitor.cs" />
- <Compile Include="IO\MemoryStreamProvider.cs" />
- <Compile Include="Localization\TextLocalizer.cs" />
- <Compile Include="Logging\ConsoleLogger.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="SystemEvents.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\Emby.Common.Implementations\Emby.Common.Implementations.csproj">
- <Project>{1e37a338-9f57-4b70-bd6d-bb9c591e319b}</Project>
- <Name>Emby.Common.Implementations</Name>
- </ProjectReference>
- <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj">
- <Project>{805844ab-e92f-45e6-9d99-4f6d48d129a5}</Project>
- <Name>Emby.Dlna</Name>
- </ProjectReference>
- <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj">
- <Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project>
- <Name>Emby.Drawing</Name>
- </ProjectReference>
- <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj">
- <Project>{89ab4548-770d-41fd-a891-8daff44f452c}</Project>
- <Name>Emby.Photos</Name>
- </ProjectReference>
- <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj">
- <Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project>
- <Name>Emby.Server.Implementations</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj">
- <Project>{4fd51ac5-2c16-4308-a993-c3a84f3b4582}</Project>
- <Name>MediaBrowser.Api</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
- <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
- <Name>MediaBrowser.Controller</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
- <Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project>
- <Name>MediaBrowser.LocalMetadata</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0bd82fa6-eb8a-4452-8af5-74f9c3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj">
- <Project>{442b5058-dcaf-4263-bb6a-f21e31120a1b}</Project>
- <Name>MediaBrowser.Providers</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
- <Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
- <Name>MediaBrowser.Server.Implementations</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj">
- <Project>{5624b7b5-b5a7-41d8-9f10-cc5611109619}</Project>
- <Name>MediaBrowser.WebDashboard</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj">
- <Project>{23499896-b135-4527-8574-c26e926ea99e}</Project>
- <Name>MediaBrowser.XbmcMetadata</Name>
- </ProjectReference>
- <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj">
- <Project>{cb7f2326-6497-4a3d-ba03-48513b17a7be}</Project>
- <Name>Mono.Nat</Name>
- </ProjectReference>
- <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
- <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
- <Name>OpenSubtitlesHandler</Name>
- </ProjectReference>
- <ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj">
- <Project>{1d74413b-e7cf-455b-b021-f52bdf881542}</Project>
- <Name>SocketHttpListener</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <None Include="app.config" />
- <None Include="packages.config" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
-</Project> \ No newline at end of file
diff --git a/Emby.Server.Core/Properties/AssemblyInfo.cs b/Emby.Server.Core/Properties/AssemblyInfo.cs
deleted file mode 100644
index ead042981..000000000
--- a/Emby.Server.Core/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Emby.Server.Core")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Emby.Server.Core")]
-[assembly: AssemblyCopyright("Copyright © 2017")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("776b9f0c-5195-45e3-9a36-1cc1f0d8e0b0")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")] \ No newline at end of file
diff --git a/Emby.Server.Core/app.config b/Emby.Server.Core/app.config
deleted file mode 100644
index 57ff62392..000000000
--- a/Emby.Server.Core/app.config
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
- <runtime>
- <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
- <dependentAssembly>
- <assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral"/>
- <bindingRedirect oldVersion="0.0.0.0-4.0.7.0" newVersion="4.0.7.0"/>
- </dependentAssembly>
- </assemblyBinding>
- </runtime>
-<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
diff --git a/Emby.Server.Core/packages.config b/Emby.Server.Core/packages.config
deleted file mode 100644
index 6311b55eb..000000000
--- a/Emby.Server.Core/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.2" targetFramework="net462" />
- <package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
- <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
-</packages> \ No newline at end of file
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 567f139fd..702917832 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Activity
{
@@ -436,7 +437,7 @@ namespace Emby.Server.Implementations.Activity
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
Type = "ScheduledTaskFailed",
- Overview = string.Join(Environment.NewLine, vals.ToArray()),
+ Overview = string.Join(Environment.NewLine, vals.ToArray(vals.Count)),
ShortOverview = runningTime,
Severity = LogSeverity.Error
});
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index e9b6f7a40..7720f8f2f 100644
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -10,6 +10,7 @@ using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using SQLitePCL.pretty;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Activity
{
@@ -94,13 +95,13 @@ namespace Emby.Server.Implementations.Activity
var whereTextWithoutPaging = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
if (startIndex.HasValue && startIndex.Value > 0)
{
var pagingWhereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})",
pagingWhereText,
@@ -109,7 +110,7 @@ namespace Emby.Server.Implementations.Activity
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -154,7 +155,7 @@ namespace Emby.Server.Implementations.Activity
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
}
- result.Items = list.ToArray();
+ result.Items = list.ToArray(list.Count);
return result;
}, ReadTransactionMode);
diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 04309e7f4..7f893d8f7 100644
--- a/Emby.Server.Core/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1,11 +1,52 @@
-using MediaBrowser.Api;
+using Emby.Common.Implementations;
+using Emby.Common.Implementations.Archiving;
+using Emby.Common.Implementations.IO;
+using Emby.Common.Implementations.Reflection;
+using Emby.Common.Implementations.ScheduledTasks;
+using Emby.Common.Implementations.Serialization;
+using Emby.Common.Implementations.TextEncoding;
+using Emby.Common.Implementations.Xml;
+using Emby.Dlna;
+using Emby.Dlna.ConnectionManager;
+using Emby.Dlna.ContentDirectory;
+using Emby.Dlna.Main;
+using Emby.Dlna.MediaReceiverRegistrar;
+using Emby.Dlna.Ssdp;
+using Emby.Drawing;
+using Emby.Photos;
+using Emby.Server.Implementations.Activity;
+using Emby.Server.Implementations.Channels;
+using Emby.Server.Implementations.Collections;
+using Emby.Server.Implementations.Configuration;
+using Emby.Server.Implementations.Data;
+using Emby.Server.Implementations.Devices;
+using Emby.Server.Implementations.Dto;
+using Emby.Server.Implementations.FFMpeg;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.HttpServer.Security;
+using Emby.Server.Implementations.IO;
+using Emby.Server.Implementations.Library;
+using Emby.Server.Implementations.LiveTv;
+using Emby.Server.Implementations.Localization;
+using Emby.Server.Implementations.MediaEncoder;
+using Emby.Server.Implementations.Migrations;
+using Emby.Server.Implementations.Notifications;
+using Emby.Server.Implementations.Playlists;
+using Emby.Server.Implementations.Security;
+using Emby.Server.Implementations.Session;
+using Emby.Server.Implementations.Social;
+using Emby.Server.Implementations.TV;
+using Emby.Server.Implementations.Updates;
+using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using Emby.Common.Implementations.ScheduledTasks;
using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
@@ -17,7 +58,9 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -35,103 +78,47 @@ using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers;
-using MediaBrowser.MediaEncoding.BdInfo;
-using MediaBrowser.MediaEncoding.Encoder;
-using MediaBrowser.MediaEncoding.Subtitles;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Reflection;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Social;
using MediaBrowser.Model.System;
+using MediaBrowser.Model.Text;
using MediaBrowser.Model.Updates;
+using MediaBrowser.Model.Xml;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
+using OpenSubtitlesHandler;
+using ServiceStack;
+using SocketHttpListener.Primitives;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Net;
-using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
-using Emby.Common.Implementations;
-using Emby.Common.Implementations.Archiving;
-using Emby.Common.Implementations.IO;
-using Emby.Common.Implementations.Reflection;
-using Emby.Common.Implementations.Serialization;
-using Emby.Common.Implementations.TextEncoding;
-using Emby.Common.Implementations.Xml;
-using Emby.Photos;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Api.Playback;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using Emby.Dlna;
-using Emby.Dlna.ConnectionManager;
-using Emby.Dlna.ContentDirectory;
-using Emby.Dlna.Main;
-using Emby.Dlna.MediaReceiverRegistrar;
-using Emby.Dlna.Ssdp;
-using Emby.Server.Core;
-using Emby.Server.Implementations.Activity;
-using Emby.Server.Implementations.Devices;
-using Emby.Server.Implementations.FFMpeg;
-using Emby.Server.Core.IO;
-using Emby.Server.Core.Localization;
-using Emby.Server.Implementations.Migrations;
-using Emby.Server.Implementations.Security;
-using Emby.Server.Implementations.Social;
-using Emby.Server.Implementations.Channels;
-using Emby.Server.Implementations.Collections;
-using Emby.Server.Implementations.Dto;
-using Emby.Server.Implementations.IO;
-using Emby.Server.Implementations.FileOrganization;
-using Emby.Server.Implementations.HttpServer;
-using Emby.Server.Implementations.HttpServer.Security;
-using Emby.Server.Implementations.Library;
-using Emby.Server.Implementations.LiveTv;
-using Emby.Server.Implementations.Localization;
-using Emby.Server.Implementations.MediaEncoder;
-using Emby.Server.Implementations.Notifications;
-using Emby.Server.Implementations.Data;
-using Emby.Server.Implementations.Playlists;
-using Emby.Server.Implementations;
-using Emby.Server.Implementations.ServerManager;
-using Emby.Server.Implementations.Session;
-using Emby.Server.Implementations.TV;
-using Emby.Server.Implementations.Updates;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.News;
-using MediaBrowser.Model.Reflection;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.Social;
-using MediaBrowser.Model.Text;
-using MediaBrowser.Model.Xml;
-using OpenSubtitlesHandler;
-using ServiceStack;
-using SocketHttpListener.Primitives;
+using Emby.Server.MediaEncoding.Subtitles;
+using MediaBrowser.MediaEncoding.BdInfo;
using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
-using Emby.Drawing;
-using Emby.Server.Implementations.Migrations;
-using MediaBrowser.Model.Diagnostics;
-using Emby.Common.Implementations.Diagnostics;
-using Emby.Server.Implementations.Configuration;
-namespace Emby.Server.Core
+namespace Emby.Server.Implementations
{
/// <summary>
/// Class CompositionRoot
@@ -216,7 +203,6 @@ namespace Emby.Server.Core
internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; }
internal IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; }
- private IFileOrganizationRepository FileOrganizationRepository { get; set; }
private INotificationManager NotificationManager { get; set; }
private ISubtitleManager SubtitleManager { get; set; }
@@ -226,7 +212,6 @@ namespace Emby.Server.Core
internal IUserViewManager UserViewManager { get; set; }
private IAuthenticationRepository AuthenticationRepository { get; set; }
- private ISyncRepository SyncRepository { get; set; }
private ITVSeriesManager TVSeriesManager { get; set; }
private ICollectionManager CollectionManager { get; set; }
private IMediaSourceManager MediaSourceManager { get; set; }
@@ -360,13 +345,6 @@ namespace Emby.Server.Core
{
var builder = GetBaseExceptionMessage(ApplicationPaths);
- // Skip if plugins haven't been loaded yet
- //if (Plugins != null)
- //{
- // var pluginString = string.Join("|", Plugins.Select(i => i.Name + "-" + i.Version.ToString()).ToArray());
- // builder.Insert(0, string.Format("Plugins: {0}{1}", pluginString, Environment.NewLine));
- //}
-
builder.Insert(0, string.Format("Version: {0}{1}", ApplicationVersion, Environment.NewLine));
builder.Insert(0, "*** Error Report ***" + Environment.NewLine);
@@ -413,7 +391,7 @@ namespace Emby.Server.Core
{
Logger.ErrorException("Error in {0}", ex, name);
}
- Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+ Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos");
}
Logger.Info("All entry points have started");
@@ -434,41 +412,41 @@ namespace Emby.Server.Core
var result = new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer"));
- ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<AudioBook>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "PlaceOfBirth", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
- ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<AudioBook>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ProviderIds" };
+ ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ProviderIds" };
return result;
}
@@ -583,9 +561,6 @@ namespace Emby.Server.Core
ItemRepository = itemRepo;
RegisterSingleInstance(ItemRepository);
- FileOrganizationRepository = GetFileOrganizationRepository();
- RegisterSingleInstance(FileOrganizationRepository);
-
AuthenticationRepository = GetAuthenticationRepository();
RegisterSingleInstance(AuthenticationRepository);
@@ -614,7 +589,7 @@ namespace Emby.Server.Core
RegisterSingleInstance(HttpServer, false);
progress.Report(10);
- ServerManager = new ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding);
+ ServerManager = new ServerManager.ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding);
RegisterSingleInstance(ServerManager);
var innerProgress = new ActionableProgress<double>();
@@ -644,9 +619,6 @@ namespace Emby.Server.Core
var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
RegisterSingleInstance<INewsService>(newsService);
- var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, LogManager.GetLogger("FileOrganizationService"), LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager, ProviderManager);
- RegisterSingleInstance<IFileOrganizationService>(fileOrganizationService);
-
progress.Report(15);
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
@@ -676,7 +648,7 @@ namespace Emby.Server.Core
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
RegisterSingleInstance(UserViewManager);
- var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder, new XmlReaderSettingsFactory());
+ var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder, new XmlReaderSettingsFactory(), TVSeriesManager);
RegisterSingleInstance<IContentDirectory>(contentDirectory);
var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager, new XmlReaderSettingsFactory());
@@ -893,7 +865,7 @@ namespace Emby.Server.Core
probePath = info.ProbePath;
var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase);
- var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"),
+ var mediaEncoder = new MediaEncoding.Encoder.MediaEncoder(LogManager.GetLogger("MediaEncoder"),
JsonSerializer,
encoderPath,
probePath,
@@ -932,19 +904,6 @@ namespace Emby.Server.Core
return repo;
}
- /// <summary>
- /// Gets the file organization repository.
- /// </summary>
- /// <returns>Task{IUserRepository}.</returns>
- private IFileOrganizationRepository GetFileOrganizationRepository()
- {
- var repo = new SqliteFileOrganizationRepository(LogManager.GetLogger("SqliteFileOrganizationRepository"), ServerConfigurationManager.ApplicationPaths);
-
- repo.Initialize();
-
- return repo;
- }
-
private IAuthenticationRepository GetAuthenticationRepository()
{
var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths);
@@ -1002,8 +961,6 @@ namespace Emby.Server.Core
BaseItem.CollectionManager = CollectionManager;
BaseItem.MediaSourceManager = MediaSourceManager;
CollectionFolder.XmlSerializer = XmlSerializer;
- BaseStreamingService.AppHost = this;
- BaseStreamingService.HttpClient = HttpClient;
Utilities.CryptographyProvider = CryptographyProvider;
AuthenticatedAttribute.AuthService = AuthService;
}
@@ -1276,7 +1233,7 @@ namespace Emby.Server.Core
list.Add(GetAssembly(typeof(InstallationManager)));
// MediaEncoding
- list.Add(GetAssembly(typeof(MediaEncoder)));
+ list.Add(GetAssembly(typeof(MediaEncoding.Encoder.MediaEncoder)));
// Dlna
list.Add(GetAssembly(typeof(DlnaEntryPoint)));
@@ -1289,10 +1246,7 @@ namespace Emby.Server.Core
list.AddRange(GetAssembliesWithPartsInternal());
- // Include composable parts in the running assembly
- list.Add(GetAssembly(typeof(ApplicationHost)));
-
- return list;
+ return list.ToList();
}
protected abstract List<Assembly> GetAssembliesWithPartsInternal();
@@ -1636,14 +1590,10 @@ namespace Emby.Server.Core
/// <returns>Task{CheckForUpdateResult}.</returns>
public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
{
- var cacheLength = TimeSpan.FromHours(3);
+ var cacheLength = TimeSpan.FromHours(1);
var updateLevel = SystemUpdateLevel;
- if (updateLevel == PackageVersionClass.Beta)
- {
- cacheLength = TimeSpan.FromHours(1);
- }
- else if (updateLevel == PackageVersionClass.Dev)
+ if (updateLevel != PackageVersionClass.Release)
{
cacheLength = TimeSpan.FromMinutes(5);
}
diff --git a/Emby.Server.Core/ApplicationPathHelper.cs b/Emby.Server.Implementations/ApplicationPathHelper.cs
index e83d5444a..262cc526e 100644
--- a/Emby.Server.Core/ApplicationPathHelper.cs
+++ b/Emby.Server.Implementations/ApplicationPathHelper.cs
@@ -2,7 +2,7 @@
using System.Configuration;
using System.IO;
-namespace Emby.Server.Core
+namespace Emby.Server.Implementations
{
public static class ApplicationPathHelper
{
diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
index f892b1e62..0c363c585 100644
--- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
@@ -18,12 +18,12 @@ namespace Emby.Server.Implementations.Channels
_channelManager = channelManager;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return GetChannel(item).GetSupportedChannelImages();
}
- public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var channel = GetChannel(item);
@@ -35,12 +35,12 @@ namespace Emby.Server.Implementations.Channels
get { return "Channel Image Provider"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Channel;
}
- private IChannel GetChannel(IHasImages item)
+ private IChannel GetChannel(IHasMetadata item)
{
var channel = (Channel)item;
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 2adf6a37c..e41e0ea87 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -159,7 +159,7 @@ namespace Emby.Server.Implementations.Channels
all = all.Take(query.Limit.Value).ToList();
}
- var returnItems = all.ToArray();
+ var returnItems = all.ToArray(all.Count);
var result = new QueryResult<Channel>
{
@@ -182,8 +182,10 @@ namespace Emby.Server.Implementations.Channels
{
};
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+ .ConfigureAwait(false));
+ var returnItems = returnList
+ .ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -567,8 +569,9 @@ namespace Emby.Server.Implementations.Channels
Fields = query.Fields.ToList()
};
- var returnItems = (await _dtoService.GetBaseItemDtos(items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(items, dtoOptions, user).ConfigureAwait(false));
+ var returnItems = returnList
+ .ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -676,12 +679,10 @@ namespace Emby.Server.Implementations.Channels
internalItems = internalItems.Take(query.Limit.Value).ToArray();
}
- var returnItemArray = internalItems.ToArray();
-
return new QueryResult<BaseItem>
{
TotalRecordCount = totalCount,
- Items = returnItemArray
+ Items = internalItems
};
}
@@ -813,12 +814,10 @@ namespace Emby.Server.Implementations.Channels
var internalItems = await Task.WhenAll(itemTasks).ConfigureAwait(false);
- var returnItemArray = internalItems.ToArray();
-
return new QueryResult<BaseItem>
{
TotalRecordCount = totalCount,
- Items = returnItemArray
+ Items = internalItems
};
}
@@ -837,8 +836,10 @@ namespace Emby.Server.Implementations.Channels
Fields = query.Fields.ToList()
};
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+ .ConfigureAwait(false));
+ var returnItems = returnList
+ .ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -989,8 +990,10 @@ namespace Emby.Server.Implementations.Channels
Fields = query.Fields.ToList()
};
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+ .ConfigureAwait(false));
+ var returnItems = returnList
+ .ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -1191,7 +1194,7 @@ namespace Emby.Server.Implementations.Channels
}
}
- var returnItemArray = all.ToArray();
+ var returnItemArray = all.ToArray(all.Count);
RefreshIfNeeded(returnItemArray);
return new QueryResult<BaseItem>
@@ -1309,7 +1312,7 @@ namespace Emby.Server.Implementations.Channels
{
item.Name = info.Name;
item.Genres = info.Genres;
- item.Studios = info.Studios;
+ item.Studios = info.Studios.ToArray(info.Studios.Count);
item.CommunityRating = info.CommunityRating;
item.Overview = info.Overview;
item.IndexNumber = info.IndexNumber;
@@ -1319,7 +1322,7 @@ namespace Emby.Server.Implementations.Channels
item.ProviderIds = info.ProviderIds;
item.OfficialRating = info.OfficialRating;
item.DateCreated = info.DateCreated ?? DateTime.UtcNow;
- item.Tags = info.Tags;
+ item.Tags = info.Tags.ToArray(info.Tags.Count);
item.HomePageUrl = info.HomePageUrl;
}
else if (info.Type == ChannelItemType.Folder && info.FolderType == ChannelFolderType.Container)
@@ -1341,7 +1344,7 @@ namespace Emby.Server.Implementations.Channels
var hasAlbumArtists = item as IHasAlbumArtist;
if (hasAlbumArtists != null)
{
- hasAlbumArtists.AlbumArtists = info.AlbumArtists;
+ hasAlbumArtists.AlbumArtists = info.AlbumArtists.ToArray(info.AlbumArtists.Count);
}
var trailer = item as Trailer;
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 463d276e5..c7378956d 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Collections
{
}
- protected override bool Supports(IHasImages item)
+ protected override bool Supports(IHasMetadata item)
{
// Right now this is the only way to prevent this image from getting created ahead of internet image providers
if (!item.IsLocked)
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Collections
return base.Supports(item);
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var playlist = (BoxSet)item;
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Collections
return GetFinalItems(items, 2);
}
- protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
}
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 4e5d344a3..5b168f6cc 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -12,6 +12,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Collections
{
@@ -190,7 +191,9 @@ namespace Emby.Server.Implementations.Collections
if (list.Count > 0)
{
- collection.LinkedChildren.AddRange(list);
+ var newList = collection.LinkedChildren.ToList();
+ newList.AddRange(list);
+ collection.LinkedChildren = newList.ToArray(newList.Count);
collection.UpdateRatingToContent();
@@ -241,9 +244,9 @@ namespace Emby.Server.Implementations.Collections
}
}
- foreach (var child in list)
+ if (list.Count > 0)
{
- collection.LinkedChildren.Remove(child);
+ collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
}
collection.UpdateRatingToContent();
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index 2241e9377..4d9bf0624 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Configuration
{
@@ -216,7 +217,7 @@ namespace Emby.Server.Implementations.Configuration
list.Add(service);
- options.DisabledMetadataSavers = list.ToArray();
+ options.DisabledMetadataSavers = list.ToArray(list.Count);
}
}
@@ -236,7 +237,7 @@ namespace Emby.Server.Implementations.Configuration
list.Add(options);
- config.MetadataOptions = list.ToArray();
+ config.MetadataOptions = list.ToArray(list.Count);
}
return options;
diff --git a/Emby.Server.Core/Cryptography/ASN1.cs b/Emby.Server.Implementations/Cryptography/ASN1.cs
index f5c826436..f5c826436 100644
--- a/Emby.Server.Core/Cryptography/ASN1.cs
+++ b/Emby.Server.Implementations/Cryptography/ASN1.cs
diff --git a/Emby.Server.Core/Cryptography/ASN1Convert.cs b/Emby.Server.Implementations/Cryptography/ASN1Convert.cs
index 851d36dc7..851d36dc7 100644
--- a/Emby.Server.Core/Cryptography/ASN1Convert.cs
+++ b/Emby.Server.Implementations/Cryptography/ASN1Convert.cs
diff --git a/Emby.Server.Core/Cryptography/BitConverterLE.cs b/Emby.Server.Implementations/Cryptography/BitConverterLE.cs
index 34e6bf6dc..34e6bf6dc 100644
--- a/Emby.Server.Core/Cryptography/BitConverterLE.cs
+++ b/Emby.Server.Implementations/Cryptography/BitConverterLE.cs
diff --git a/Emby.Server.Core/Cryptography/CertificateGenerator.cs b/Emby.Server.Implementations/Cryptography/CertificateGenerator.cs
index 2600d7470..2600d7470 100644
--- a/Emby.Server.Core/Cryptography/CertificateGenerator.cs
+++ b/Emby.Server.Implementations/Cryptography/CertificateGenerator.cs
diff --git a/Emby.Server.Core/Cryptography/CryptoConvert.cs b/Emby.Server.Implementations/Cryptography/CryptoConvert.cs
index 70a91bfff..70a91bfff 100644
--- a/Emby.Server.Core/Cryptography/CryptoConvert.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptoConvert.cs
diff --git a/Emby.Server.Core/Cryptography/PKCS1.cs b/Emby.Server.Implementations/Cryptography/PKCS1.cs
index 24c0708c5..24c0708c5 100644
--- a/Emby.Server.Core/Cryptography/PKCS1.cs
+++ b/Emby.Server.Implementations/Cryptography/PKCS1.cs
diff --git a/Emby.Server.Core/Cryptography/PKCS12.cs b/Emby.Server.Implementations/Cryptography/PKCS12.cs
index 50f3776d9..50f3776d9 100644
--- a/Emby.Server.Core/Cryptography/PKCS12.cs
+++ b/Emby.Server.Implementations/Cryptography/PKCS12.cs
diff --git a/Emby.Server.Core/Cryptography/PKCS7.cs b/Emby.Server.Implementations/Cryptography/PKCS7.cs
index 475854500..475854500 100644
--- a/Emby.Server.Core/Cryptography/PKCS7.cs
+++ b/Emby.Server.Implementations/Cryptography/PKCS7.cs
diff --git a/Emby.Server.Core/Cryptography/PKCS8.cs b/Emby.Server.Implementations/Cryptography/PKCS8.cs
index 7e9a27298..7e9a27298 100644
--- a/Emby.Server.Core/Cryptography/PKCS8.cs
+++ b/Emby.Server.Implementations/Cryptography/PKCS8.cs
diff --git a/Emby.Server.Core/Cryptography/PfxGenerator.cs b/Emby.Server.Implementations/Cryptography/PfxGenerator.cs
index 2d1dd649e..2d1dd649e 100644
--- a/Emby.Server.Core/Cryptography/PfxGenerator.cs
+++ b/Emby.Server.Implementations/Cryptography/PfxGenerator.cs
diff --git a/Emby.Server.Core/Cryptography/X501Name.cs b/Emby.Server.Implementations/Cryptography/X501Name.cs
index 3318f95e2..3318f95e2 100644
--- a/Emby.Server.Core/Cryptography/X501Name.cs
+++ b/Emby.Server.Implementations/Cryptography/X501Name.cs
diff --git a/Emby.Server.Core/Cryptography/X509Builder.cs b/Emby.Server.Implementations/Cryptography/X509Builder.cs
index a2e292350..a2e292350 100644
--- a/Emby.Server.Core/Cryptography/X509Builder.cs
+++ b/Emby.Server.Implementations/Cryptography/X509Builder.cs
diff --git a/Emby.Server.Core/Cryptography/X509Certificate.cs b/Emby.Server.Implementations/Cryptography/X509Certificate.cs
index 3de58cfee..3de58cfee 100644
--- a/Emby.Server.Core/Cryptography/X509Certificate.cs
+++ b/Emby.Server.Implementations/Cryptography/X509Certificate.cs
diff --git a/Emby.Server.Core/Cryptography/X509CertificateBuilder.cs b/Emby.Server.Implementations/Cryptography/X509CertificateBuilder.cs
index cecff6578..cecff6578 100644
--- a/Emby.Server.Core/Cryptography/X509CertificateBuilder.cs
+++ b/Emby.Server.Implementations/Cryptography/X509CertificateBuilder.cs
diff --git a/Emby.Server.Core/Cryptography/X509CertificateCollection.cs b/Emby.Server.Implementations/Cryptography/X509CertificateCollection.cs
index a129bfc1a..a129bfc1a 100644
--- a/Emby.Server.Core/Cryptography/X509CertificateCollection.cs
+++ b/Emby.Server.Implementations/Cryptography/X509CertificateCollection.cs
diff --git a/Emby.Server.Core/Cryptography/X509Extension.cs b/Emby.Server.Implementations/Cryptography/X509Extension.cs
index 36b17deba..36b17deba 100644
--- a/Emby.Server.Core/Cryptography/X509Extension.cs
+++ b/Emby.Server.Implementations/Cryptography/X509Extension.cs
diff --git a/Emby.Server.Core/Cryptography/X509Extensions.cs b/Emby.Server.Implementations/Cryptography/X509Extensions.cs
index 018a04d79..018a04d79 100644
--- a/Emby.Server.Core/Cryptography/X509Extensions.cs
+++ b/Emby.Server.Implementations/Cryptography/X509Extensions.cs
diff --git a/Emby.Server.Core/Cryptography/X520Attributes.cs b/Emby.Server.Implementations/Cryptography/X520Attributes.cs
index da7fd2b82..da7fd2b82 100644
--- a/Emby.Server.Core/Cryptography/X520Attributes.cs
+++ b/Emby.Server.Implementations/Cryptography/X520Attributes.cs
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 462ff3e47..a34c90cb4 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -131,25 +131,11 @@ namespace Emby.Server.Implementations.Data
{
queries.Add("PRAGMA temp_store = memory");
}
-
- ////foreach (var query in queries)
- ////{
- //// db.Execute(query);
- ////}
-
- //Logger.Info("synchronous: " + db.Query("PRAGMA synchronous").SelectScalarString().First());
- //Logger.Info("temp_store: " + db.Query("PRAGMA temp_store").SelectScalarString().First());
-
- /*if (!string.Equals(_defaultWal, "wal", StringComparison.OrdinalIgnoreCase))
+ else
{
- queries.Add("PRAGMA journal_mode=WAL");
-
- using (WriteLock.Write())
- {
- db.ExecuteAll(string.Join(";", queries.ToArray()));
- }
+ queries.Add("PRAGMA temp_store = file");
}
- else*/
+
foreach (var query in queries)
{
db.Execute(query);
@@ -208,6 +194,13 @@ namespace Emby.Server.Implementations.Data
"pragma temp_store = memory"
});
}
+ else
+ {
+ queries.AddRange(new List<string>
+ {
+ "pragma temp_store = file"
+ });
+ }
db.ExecuteAll(string.Join(";", queries.ToArray()));
Logger.Info("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index f43e45441..37ba2eb5f 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -11,9 +11,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.Data
{
@@ -34,31 +31,9 @@ namespace Emby.Server.Implementations.Data
_appPaths = appPaths;
}
- public string Name
+ public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- get { return "Clean Database"; }
- }
-
- public string Description
- {
- get { return "Deletes obsolete content from the database."; }
- }
-
- public string Category
- {
- get { return "Library"; }
- }
-
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- // Ensure these objects are lazy loaded.
- // Without this there is a deadlock that will need to be investigated
- var rootChildren = _libraryManager.RootFolder.Children.ToList();
- rootChildren = _libraryManager.GetUserRootFolder().Children.ToList();
-
- await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
-
- //await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false);
+ return CleanDeadItems(cancellationToken, progress);
}
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
@@ -98,23 +73,5 @@ namespace Emby.Server.Implementations.Data
progress.Report(100);
}
-
- /// <summary>
- /// 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}
- };
- }
-
- public string Key
- {
- get { return "CleanDatabase"; }
- }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs b/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs
deleted file mode 100644
index a254962c9..000000000
--- a/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs
+++ /dev/null
@@ -1,284 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Data
-{
- public class SqliteFileOrganizationRepository : BaseSqliteRepository, IFileOrganizationRepository, IDisposable
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public SqliteFileOrganizationRepository(ILogger logger, IServerApplicationPaths appPaths) : base(logger)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "fileorganization.db");
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public void Initialize()
- {
- using (var connection = CreateConnection())
- {
- RunDefaultInitialization(connection);
-
- string[] queries = {
-
- "create table if not exists FileOrganizerResults (ResultId GUID PRIMARY KEY, OriginalPath TEXT, TargetPath TEXT, FileLength INT, OrganizationDate datetime, Status TEXT, OrganizationType TEXT, StatusMessage TEXT, ExtractedName TEXT, ExtractedYear int null, ExtractedSeasonNumber int null, ExtractedEpisodeNumber int null, ExtractedEndingEpisodeNumber, DuplicatePaths TEXT int null)",
- "create index if not exists idx_FileOrganizerResults on FileOrganizerResults(ResultId)"
- };
-
- connection.RunQueries(queries);
- }
- }
-
- public async Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
- {
- if (result == null)
- {
- throw new ArgumentNullException("result");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- var commandText = "replace into FileOrganizerResults (ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths) values (@ResultId, @OriginalPath, @TargetPath, @FileLength, @OrganizationDate, @Status, @OrganizationType, @StatusMessage, @ExtractedName, @ExtractedYear, @ExtractedSeasonNumber, @ExtractedEpisodeNumber, @ExtractedEndingEpisodeNumber, @DuplicatePaths)";
-
- using (var statement = db.PrepareStatement(commandText))
- {
- statement.TryBind("@ResultId", result.Id.ToGuidBlob());
- statement.TryBind("@OriginalPath", result.OriginalPath);
-
- statement.TryBind("@TargetPath", result.TargetPath);
- statement.TryBind("@FileLength", result.FileSize);
- statement.TryBind("@OrganizationDate", result.Date.ToDateTimeParamValue());
- statement.TryBind("@Status", result.Status.ToString());
- statement.TryBind("@OrganizationType", result.Type.ToString());
- statement.TryBind("@StatusMessage", result.StatusMessage);
- statement.TryBind("@ExtractedName", result.ExtractedName);
- statement.TryBind("@ExtractedYear", result.ExtractedYear);
- statement.TryBind("@ExtractedSeasonNumber", result.ExtractedSeasonNumber);
- statement.TryBind("@ExtractedEpisodeNumber", result.ExtractedEpisodeNumber);
- statement.TryBind("@ExtractedEndingEpisodeNumber", result.ExtractedEndingEpisodeNumber);
- statement.TryBind("@DuplicatePaths", string.Join("|", result.DuplicatePaths.ToArray()));
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- public async Task Delete(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
- {
- statement.TryBind("@ResultId", id.ToGuidBlob());
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- public async Task DeleteAll()
- {
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- var commandText = "delete from FileOrganizerResults";
-
- db.Execute(commandText);
- }, TransactionMode);
- }
- }
- }
-
- public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
-
- if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
- {
- commandText += string.Format(" WHERE ResultId NOT IN (SELECT ResultId FROM FileOrganizerResults ORDER BY OrganizationDate desc LIMIT {0})",
- query.StartIndex.Value.ToString(_usCulture));
- }
-
- commandText += " ORDER BY OrganizationDate desc";
-
- if (query.Limit.HasValue)
- {
- commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- var list = new List<FileOrganizationResult>();
-
- using (var statement = connection.PrepareStatement(commandText))
- {
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(GetResult(row));
- }
- }
-
- int count;
- using (var statement = connection.PrepareStatement("select count (ResultId) from FileOrganizerResults"))
- {
- count = statement.ExecuteQuery().SelectScalarInt().First();
- }
-
- return new QueryResult<FileOrganizationResult>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public FileOrganizationResult GetResult(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- using (var statement = connection.PrepareStatement("select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@ResultId"))
- {
- statement.TryBind("@ResultId", id.ToGuidBlob());
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetResult(row);
- }
- }
-
- return null;
- }
- }
- }
-
- public FileOrganizationResult GetResult(IReadOnlyList<IResultSetValue> reader)
- {
- var index = 0;
-
- var result = new FileOrganizationResult
- {
- Id = reader[0].ReadGuidFromBlob().ToString("N")
- };
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.OriginalPath = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.TargetPath = reader[index].ToString();
- }
-
- index++;
- result.FileSize = reader[index].ToInt64();
-
- index++;
- result.Date = reader[index].ReadDateTime();
-
- index++;
- result.Status = (FileSortingStatus)Enum.Parse(typeof(FileSortingStatus), reader[index].ToString(), true);
-
- index++;
- result.Type = (FileOrganizerType)Enum.Parse(typeof(FileOrganizerType), reader[index].ToString(), true);
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.StatusMessage = reader[index].ToString();
- }
-
- result.OriginalFileName = Path.GetFileName(result.OriginalPath);
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.ExtractedName = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.ExtractedYear = reader[index].ToInt();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.ExtractedSeasonNumber = reader[index].ToInt();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.ExtractedEpisodeNumber = reader[index].ToInt();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.ExtractedEndingEpisodeNumber = reader[index].ToInt();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- result.DuplicatePaths = reader[index].ToString().Split('|').Where(i => !string.IsNullOrEmpty(i)).ToList();
- }
-
- return result;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 52abbae3e..fc47809ac 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -31,6 +31,7 @@ using MediaBrowser.Model.Reflection;
using SQLitePCL.pretty;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Data
{
@@ -190,7 +191,6 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "IsLocked", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Name", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "OfficialRating", "Text", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "MediaType", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Overview", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ParentIndexNumber", "INT", existingColumnNames);
@@ -200,28 +200,22 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "Genres", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "SortName", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ForcedSortName", "Text", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "RunTimeTicks", "BIGINT", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "HomePageUrl", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "DisplayMediaType", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "DateCreated", "DATETIME", existingColumnNames);
AddColumn(db, "TypedBaseItems", "DateModified", "DATETIME", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "IsSeries", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsLive", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsNews", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsPremiere", "BIT", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "EpisodeTitle", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsRepeat", "BIT", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "PreferredMetadataLanguage", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "PreferredMetadataCountryCode", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsHD", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ExternalEtag", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "DateLastRefreshed", "DATETIME", existingColumnNames);
-
AddColumn(db, "TypedBaseItems", "DateLastSaved", "DATETIME", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsInMixedFolder", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "LockedFields", "Text", existingColumnNames);
@@ -233,10 +227,8 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "InheritedParentalRatingValue", "INT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "UnratedType", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "TopParentId", "Text", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "IsItemByName", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "TrailerTypes", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "CriticRating", "Float", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "InheritedTags", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "CleanName", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "PresentationUniqueKey", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "OriginalTitle", "Text", existingColumnNames);
@@ -251,7 +243,6 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "SeriesId", "GUID", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ExternalSeriesId", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Tagline", "Text", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "Keywords", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ProviderIds", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Images", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ProductionLocations", "Text", existingColumnNames);
@@ -354,7 +345,10 @@ namespace Emby.Server.Implementations.Data
// items by name
"create index if not exists idx_ItemValues6 on ItemValues(ItemId,Type,CleanValue)",
- "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)"
+ "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)",
+
+ // Used to update inherited tags
+ "create index if not exists idx_ItemValues8 on ItemValues(Type, ItemId, Value)",
};
connection.RunQueries(postQueries);
@@ -451,10 +445,8 @@ namespace Emby.Server.Implementations.Data
"SeriesId",
"PresentationUniqueKey",
"InheritedParentalRatingValue",
- "InheritedTags",
"ExternalSeriesId",
"Tagline",
- "Keywords",
"ProviderIds",
"Images",
"ProductionLocations",
@@ -560,10 +552,8 @@ namespace Emby.Server.Implementations.Data
"IsFolder",
"UnratedType",
"TopParentId",
- "IsItemByName",
"TrailerTypes",
"CriticRating",
- "InheritedTags",
"CleanName",
"PresentationUniqueKey",
"OriginalTitle",
@@ -578,7 +568,6 @@ namespace Emby.Server.Implementations.Data
"SeriesId",
"ExternalSeriesId",
"Tagline",
- "Keywords",
"ProviderIds",
"Images",
"ProductionLocations",
@@ -645,7 +634,7 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
- var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string>>();
+ var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>>();
foreach (var item in items)
{
var ancestorIds = item.SupportsAncestors ?
@@ -655,8 +644,9 @@ namespace Emby.Server.Implementations.Data
var topParent = item.GetTopParent();
var userdataKey = item.GetUserDataKeys().FirstOrDefault();
+ var inheritedTags = item.GetInheritedTags();
- tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string>(item, ancestorIds, topParent, userdataKey));
+ tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>(item, ancestorIds, topParent, userdataKey, inheritedTags));
}
using (WriteLock.Write())
@@ -666,12 +656,13 @@ namespace Emby.Server.Implementations.Data
connection.RunInTransaction(db =>
{
SaveItemsInTranscation(db, tuples);
+
}, TransactionMode);
}
}
}
- private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string>> tuples)
+ private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>> tuples)
{
var requiresReset = false;
@@ -702,12 +693,14 @@ namespace Emby.Server.Implementations.Data
SaveItem(item, topParent, userDataKey, saveItemStatement);
//Logger.Debug(_saveItemCommand.CommandText);
+ var inheritedTags = tuple.Item5;
+
if (item.SupportsAncestors)
{
UpdateAncestors(item.Id, tuple.Item2, db, deleteAncestorsStatement, updateAncestorsStatement);
}
- UpdateItemValues(item.Id, GetItemValuesToSave(item), db);
+ UpdateItemValues(item.Id, GetItemValuesToSave(item, inheritedTags), db);
requiresReset = true;
}
@@ -818,7 +811,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@RunTimeTicks", item.RunTimeTicks);
saveItemStatement.TryBind("@HomePageUrl", item.HomePageUrl);
- saveItemStatement.TryBind("@DisplayMediaType", item.DisplayMediaType);
+ saveItemStatement.TryBindNull("@DisplayMediaType");
saveItemStatement.TryBind("@DateCreated", item.DateCreated);
saveItemStatement.TryBind("@DateModified", item.DateModified);
@@ -847,7 +840,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@IsInMixedFolder", item.IsInMixedFolder);
- if (item.LockedFields.Count > 0)
+ if (item.LockedFields.Length > 0)
{
saveItemStatement.TryBind("@LockedFields", string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()));
}
@@ -856,7 +849,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@LockedFields");
}
- if (item.Studios.Count > 0)
+ if (item.Studios.Length > 0)
{
saveItemStatement.TryBind("@Studios", string.Join("|", item.Studios.ToArray()));
}
@@ -876,9 +869,9 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ExternalServiceId", item.ServiceName);
- if (item.Tags.Count > 0)
+ if (item.Tags.Length > 0)
{
- saveItemStatement.TryBind("@Tags", string.Join("|", item.Tags.ToArray()));
+ saveItemStatement.TryBind("@Tags", string.Join("|", item.Tags));
}
else
{
@@ -900,15 +893,6 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@TopParentId");
}
- var isByName = false;
- var byName = item as IItemByName;
- if (byName != null)
- {
- var dualAccess = item as IHasDualAccess;
- isByName = dualAccess == null || dualAccess.IsAccessedByName;
- }
- saveItemStatement.TryBind("@IsItemByName", isByName);
-
var trailer = item as Trailer;
if (trailer != null && trailer.TrailerTypes.Count > 0)
{
@@ -921,16 +905,6 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@CriticRating", item.CriticRating);
- var inheritedTags = item.InheritedTags;
- if (inheritedTags.Count > 0)
- {
- saveItemStatement.TryBind("@InheritedTags", string.Join("|", inheritedTags.ToArray()));
- }
- else
- {
- saveItemStatement.TryBindNull("@InheritedTags");
- }
-
if (string.IsNullOrWhiteSpace(item.Name))
{
saveItemStatement.TryBindNull("@CleanName");
@@ -1011,28 +985,19 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId);
saveItemStatement.TryBind("@Tagline", item.Tagline);
- if (item.Keywords.Count > 0)
- {
- saveItemStatement.TryBind("@Keywords", string.Join("|", item.Keywords.ToArray()));
- }
- else
- {
- saveItemStatement.TryBindNull("@Keywords");
- }
-
saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item));
saveItemStatement.TryBind("@Images", SerializeImages(item));
- if (item.ProductionLocations.Count > 0)
+ if (item.ProductionLocations.Length > 0)
{
- saveItemStatement.TryBind("@ProductionLocations", string.Join("|", item.ProductionLocations.ToArray()));
+ saveItemStatement.TryBind("@ProductionLocations", string.Join("|", item.ProductionLocations));
}
else
{
saveItemStatement.TryBindNull("@ProductionLocations");
}
- if (item.ThemeSongIds.Count > 0)
+ if (item.ThemeSongIds.Length > 0)
{
saveItemStatement.TryBind("@ThemeSongIds", string.Join("|", item.ThemeSongIds.ToArray()));
}
@@ -1041,7 +1006,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@ThemeSongIds");
}
- if (item.ThemeVideoIds.Count > 0)
+ if (item.ThemeVideoIds.Length > 0)
{
saveItemStatement.TryBind("@ThemeVideoIds", string.Join("|", item.ThemeVideoIds.ToArray()));
}
@@ -1075,9 +1040,9 @@ namespace Emby.Server.Implementations.Data
var hasAlbumArtists = item as IHasAlbumArtist;
if (hasAlbumArtists != null)
{
- if (hasAlbumArtists.AlbumArtists.Count > 0)
+ if (hasAlbumArtists.AlbumArtists.Length > 0)
{
- albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists.ToArray());
+ albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists);
}
}
saveItemStatement.TryBind("@AlbumArtists", albumArtists);
@@ -1128,9 +1093,9 @@ namespace Emby.Server.Implementations.Data
private string SerializeImages(BaseItem item)
{
- var images = item.ImageInfos.ToList();
+ var images = item.ImageInfos;
- if (images.Count == 0)
+ if (images.Length == 0)
{
return null;
}
@@ -1147,22 +1112,24 @@ namespace Emby.Server.Implementations.Data
return;
}
- if (item.ImageInfos.Count > 0)
+ if (item.ImageInfos.Length > 0)
{
return;
}
var parts = value.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
-
+ var list = new List<ItemImageInfo>();
foreach (var part in parts)
{
var image = ItemImageInfoFromValueString(part);
if (image != null)
{
- item.ImageInfos.Add(image);
+ list.Add(image);
}
}
+
+ item.ImageInfos = list.ToArray(list.Count);
}
public string ToValueString(ItemImageInfo image)
@@ -1329,16 +1296,13 @@ namespace Emby.Server.Implementations.Data
return false;
}
- if (_config.Configuration.SkipDeserializationForAudio)
+ if (type == typeof(Audio))
{
- if (type == typeof(Audio))
- {
- return false;
- }
- if (type == typeof(MusicAlbum))
- {
- return false;
- }
+ return false;
+ }
+ if (type == typeof(MusicAlbum))
+ {
+ return false;
}
return true;
@@ -1609,11 +1573,15 @@ namespace Emby.Server.Implementations.Data
index++;
}
+ var video = item as Video;
if (HasField(query, ItemFields.DisplayMediaType))
{
- if (!reader.IsDBNull(index))
+ if (video != null)
{
- item.DisplayMediaType = reader.GetString(index);
+ if (!reader.IsDBNull(index))
+ {
+ video.DisplayMediaType = reader.GetString(index);
+ }
}
index++;
}
@@ -1668,7 +1636,11 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- item.Audio = (ProgramAudio)Enum.Parse(typeof(ProgramAudio), reader.GetString(index), true);
+ ProgramAudio audio;
+ if (Enum.TryParse(reader.GetString(index), true, out audio))
+ {
+ item.Audio = audio;
+ }
}
index++;
@@ -1699,7 +1671,17 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.LockedFields = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => (MetadataFields)Enum.Parse(typeof(MetadataFields), i, true)).ToList();
+ item.LockedFields = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(
+ i =>
+ {
+ MetadataFields parsedValue;
+
+ if (Enum.TryParse(i, true, out parsedValue))
+ {
+ return parsedValue;
+ }
+ return (MetadataFields?)null;
+ }).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
index++;
}
@@ -1708,7 +1690,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.Studios = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ item.Studios = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1717,7 +1699,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.Tags = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ item.Tags = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1729,7 +1711,18 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- trailer.TrailerTypes = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => (TrailerType)Enum.Parse(typeof(TrailerType), i, true)).ToList();
+ trailer.TrailerTypes = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(
+ i =>
+ {
+ TrailerType parsedValue;
+
+ if (Enum.TryParse(i, true, out parsedValue))
+ {
+ return parsedValue;
+ }
+ return (TrailerType?)null;
+
+ }).Where(i => i.HasValue).Select(i => i.Value).ToList();
}
}
index++;
@@ -1744,7 +1737,6 @@ namespace Emby.Server.Implementations.Data
index++;
}
- var video = item as Video;
if (video != null)
{
if (!reader.IsDBNull(index))
@@ -1847,15 +1839,6 @@ namespace Emby.Server.Implementations.Data
index++;
}
- if (HasField(query, ItemFields.Tags))
- {
- if (!reader.IsDBNull(index))
- {
- item.InheritedTags = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- }
- index++;
- }
-
if (HasField(query, ItemFields.ExternalSeriesId))
{
if (!reader.IsDBNull(index))
@@ -1874,15 +1857,6 @@ namespace Emby.Server.Implementations.Data
index++;
}
- if (HasField(query, ItemFields.Keywords))
- {
- if (!reader.IsDBNull(index))
- {
- item.Keywords = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- }
- index++;
- }
-
if (!reader.IsDBNull(index))
{
DeserializeProviderIds(reader.GetString(index), item);
@@ -1902,7 +1876,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.ProductionLocations = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ item.ProductionLocations = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
}
index++;
}
@@ -1911,7 +1885,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.ThemeSongIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
+ item.ThemeSongIds = SplitToGuids(reader.GetString(index));
}
index++;
}
@@ -1920,7 +1894,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.ThemeVideoIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
+ item.ThemeVideoIds = SplitToGuids(reader.GetString(index));
}
index++;
}
@@ -1933,7 +1907,11 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- item.ExtraType = (ExtraType)Enum.Parse(typeof(ExtraType), reader.GetString(index), true);
+ ExtraType extraType;
+ if (Enum.TryParse(reader.GetString(index), true, out extraType))
+ {
+ item.ExtraType = extraType;
+ }
}
index++;
@@ -1949,7 +1927,7 @@ namespace Emby.Server.Implementations.Data
var hasAlbumArtists = item as IHasAlbumArtist;
if (hasAlbumArtists != null && !reader.IsDBNull(index))
{
- hasAlbumArtists.AlbumArtists = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ hasAlbumArtists.AlbumArtists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1975,27 +1953,28 @@ namespace Emby.Server.Implementations.Data
return item;
}
+ private Guid[] SplitToGuids(string value)
+ {
+ var ids = value.Split('|');
+
+ var result = new Guid[ids.Length];
+
+ for (var i = 0; i < result.Length; i++)
+ {
+ result[i] = new Guid(ids[i]);
+ }
+
+ return result;
+ }
+
/// <summary>
/// Gets the critic reviews.
/// </summary>
/// <param name="itemId">The item id.</param>
/// <returns>Task{IEnumerable{ItemReview}}.</returns>
- public IEnumerable<ItemReview> GetCriticReviews(Guid itemId)
+ public List<ItemReview> GetCriticReviews(Guid itemId)
{
- try
- {
- var path = Path.Combine(_criticReviewsPath, itemId + ".json");
-
- return _jsonSerializer.DeserializeFromFile<List<ItemReview>>(path);
- }
- catch (FileNotFoundException)
- {
- return new List<ItemReview>();
- }
- catch (IOException)
- {
- return new List<ItemReview>();
- }
+ return new List<ItemReview>();
}
private readonly Task _cachedTask = Task.FromResult(true);
@@ -2007,12 +1986,6 @@ namespace Emby.Server.Implementations.Data
/// <returns>Task.</returns>
public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
{
- _fileSystem.CreateDirectory(_criticReviewsPath);
-
- var path = Path.Combine(_criticReviewsPath, itemId + ".json");
-
- _jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
-
return _cachedTask;
}
@@ -2022,7 +1995,7 @@ namespace Emby.Server.Implementations.Data
/// <param name="id">The id.</param>
/// <returns>IEnumerable{ChapterInfo}.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
- public IEnumerable<ChapterInfo> GetChapters(Guid id)
+ public List<ChapterInfo> GetChapters(Guid id)
{
CheckDisposed();
if (id == Guid.Empty)
@@ -2118,18 +2091,7 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Saves the chapters.
/// </summary>
- /// <param name="id">The id.</param>
- /// <param name="chapters">The chapters.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">
- /// id
- /// or
- /// chapters
- /// or
- /// cancellationToken
- /// </exception>
- public async Task SaveChapters(Guid id, List<ChapterInfo> chapters, CancellationToken cancellationToken)
+ public async Task SaveChapters(Guid id, List<ChapterInfo> chapters)
{
CheckDisposed();
@@ -2143,8 +2105,6 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException("chapters");
}
- cancellationToken.ThrowIfCancellationRequested();
-
var index = 0;
using (WriteLock.Write())
@@ -2250,7 +2210,7 @@ namespace Emby.Server.Implementations.Data
return false;
}
- private List<ItemFields> allFields = Enum.GetNames(typeof(ItemFields))
+ private readonly List<ItemFields> allFields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
.ToList();
@@ -2274,7 +2234,7 @@ namespace Emby.Server.Implementations.Data
}
if (field == ItemFields.Tags)
{
- return new[] { "Tags", "InheritedTags" };
+ return new[] { "Tags" };
}
return new[] { field.ToString() };
@@ -2287,9 +2247,8 @@ namespace Emby.Server.Implementations.Data
switch (name)
{
case ItemFields.HomePageUrl:
- case ItemFields.Keywords:
- case ItemFields.DisplayMediaType:
case ItemFields.CustomRating:
+ case ItemFields.DisplayMediaType:
case ItemFields.ProductionLocations:
case ItemFields.Settings:
case ItemFields.OriginalTitle:
@@ -2593,11 +2552,11 @@ namespace Emby.Server.Implementations.Data
}
}
- query.ExcludeItemIds = excludeIds.ToArray();
+ query.ExcludeItemIds = excludeIds.ToArray(excludeIds.Count);
query.ExcludeProviderIds = item.ProviderIds;
}
- return list.ToArray();
+ return list.ToArray(list.Count);
}
private void BindSimilarParams(InternalItemsQuery query, IStatement statement)
@@ -2633,9 +2592,14 @@ namespace Emby.Server.Implementations.Data
groups.Add("PresentationUniqueKey");
}
+ if (query.GroupBySeriesPresentationUniqueKey)
+ {
+ groups.Add("SeriesPresentationUniqueKey");
+ }
+
if (groups.Count > 0)
{
- return " Group by " + string.Join(",", groups.ToArray());
+ return " Group by " + string.Join(",", groups.ToArray(groups.Count));
}
return string.Empty;
@@ -2672,7 +2636,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -2729,7 +2693,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -2849,7 +2813,7 @@ namespace Emby.Server.Implementations.Data
var slowThreshold = 1000;
#if DEBUG
- slowThreshold = 2;
+ slowThreshold = 10;
#endif
if (elapsed >= slowThreshold)
@@ -2882,7 +2846,7 @@ namespace Emby.Server.Implementations.Data
var returnList = GetItemList(query);
return new QueryResult<BaseItem>
{
- Items = returnList.ToArray(),
+ Items = returnList.ToArray(returnList.Count),
TotalRecordCount = returnList.Count
};
}
@@ -2905,7 +2869,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
var whereTextWithoutPaging = whereText;
@@ -2945,6 +2909,10 @@ namespace Emby.Server.Implementations.Data
{
commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
}
+ else if (query.GroupBySeriesPresentationUniqueKey)
+ {
+ commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText();
+ }
else
{
commandText += " select count (guid)" + GetFromText();
@@ -2962,8 +2930,7 @@ namespace Emby.Server.Implementations.Data
return connection.RunInTransaction(db =>
{
var result = new QueryResult<BaseItem>();
- var statements = PrepareAllSafe(db, statementTexts)
- .ToList();
+ var statements = PrepareAllSafe(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -3017,7 +2984,7 @@ namespace Emby.Server.Implementations.Data
LogQueryTime("GetItems", commandText, now);
- result.Items = list.ToArray();
+ result.Items = list.ToArray(list.Count);
return result;
}, ReadTransactionMode);
@@ -3090,6 +3057,11 @@ namespace Emby.Server.Implementations.Data
}
if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase))
{
+ if (query.GroupBySeriesPresentationUniqueKey)
+ {
+ return new Tuple<string, bool>("MAX(LastPlayedDate)", false);
+ }
+
return new Tuple<string, bool>("LastPlayedDate", false);
}
if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase))
@@ -3164,7 +3136,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -3235,7 +3207,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -3308,7 +3280,7 @@ namespace Emby.Server.Implementations.Data
var returnList = GetItemIdsList(query);
return new QueryResult<Guid>
{
- Items = returnList.ToArray(),
+ Items = returnList.ToArray(returnList.Count),
TotalRecordCount = returnList.Count
};
}
@@ -3323,7 +3295,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
var whereTextWithoutPaging = whereText;
@@ -3364,6 +3336,10 @@ namespace Emby.Server.Implementations.Data
{
commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
}
+ else if (query.GroupBySeriesPresentationUniqueKey)
+ {
+ commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText();
+ }
else
{
commandText += " select count (guid)" + GetFromText();
@@ -3382,8 +3358,7 @@ namespace Emby.Server.Implementations.Data
{
var result = new QueryResult<Guid>();
- var statements = PrepareAllSafe(db, statementTexts)
- .ToList();
+ var statements = PrepareAllSafe(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -3426,7 +3401,7 @@ namespace Emby.Server.Implementations.Data
LogQueryTime("GetItemIds", commandText, now);
- result.Items = list.ToArray();
+ result.Items = list.ToArray(list.Count);
return result;
}, ReadTransactionMode);
@@ -3631,7 +3606,7 @@ namespace Emby.Server.Implementations.Data
}
if (programAttribtues.Count > 0)
{
- whereClauses.Add("(" + string.Join(" OR ", programAttribtues.ToArray()) + ")");
+ whereClauses.Add("(" + string.Join(" OR ", programAttribtues.ToArray(programAttribtues.Count)) + ")");
}
}
@@ -4221,23 +4196,6 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(clause);
}
- if (query.Keywords.Length > 0)
- {
- var clauses = new List<string>();
- var index = 0;
- foreach (var item in query.Keywords)
- {
- clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)");
- if (statement != null)
- {
- statement.TryBind("@Keyword" + index, GetCleanValue(item));
- }
- index++;
- }
- var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
- whereClauses.Add(clause);
- }
-
if (query.OfficialRatings.Length > 0)
{
var clauses = new List<string>();
@@ -4317,12 +4275,13 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ProductionYear in (" + val + ")");
}
- if (query.IsVirtualItem.HasValue)
+ var isVirtualItem = query.IsVirtualItem ?? query.IsMissing;
+ if (isVirtualItem.HasValue)
{
whereClauses.Add("IsVirtualItem=@IsVirtualItem");
if (statement != null)
{
- statement.TryBind("@IsVirtualItem", query.IsVirtualItem.Value);
+ statement.TryBind("@IsVirtualItem", isVirtualItem.Value);
}
}
if (query.IsSpecialSeason.HasValue)
@@ -4347,28 +4306,6 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("PremiereDate < DATETIME('now')");
}
}
- if (query.IsMissing.HasValue)
- {
- if (query.IsMissing.Value)
- {
- whereClauses.Add("(IsVirtualItem=1 AND PremiereDate < DATETIME('now'))");
- }
- else
- {
- whereClauses.Add("(IsVirtualItem=0 OR PremiereDate >= DATETIME('now'))");
- }
- }
- if (query.IsVirtualUnaired.HasValue)
- {
- if (query.IsVirtualUnaired.Value)
- {
- whereClauses.Add("(IsVirtualItem=1 AND PremiereDate >= DATETIME('now'))");
- }
- else
- {
- whereClauses.Add("(IsVirtualItem=0 OR PremiereDate < DATETIME('now'))");
- }
- }
var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray();
if (queryMediaTypes.Length == 1)
{
@@ -4483,21 +4420,27 @@ namespace Emby.Server.Implementations.Data
}
}
- //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0;
- var enableItemsByName = query.IncludeItemsByName ?? false;
+
+ var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList();
+ var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
var queryTopParentIds = query.TopParentIds.Where(IsValidId).ToArray();
if (queryTopParentIds.Length == 1)
{
- if (enableItemsByName)
+ if (enableItemsByName && includedItemByNameTypes.Count == 1)
{
- whereClauses.Add("(TopParentId=@TopParentId or IsItemByName=@IsItemByName)");
+ whereClauses.Add("(TopParentId=@TopParentId or Type=@IncludedItemByNameType)");
if (statement != null)
{
- statement.TryBind("@IsItemByName", true);
+ statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]);
}
}
+ else if (enableItemsByName && includedItemByNameTypes.Count > 1)
+ {
+ var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'").ToArray());
+ whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))");
+ }
else
{
whereClauses.Add("(TopParentId=@TopParentId)");
@@ -4511,14 +4454,19 @@ namespace Emby.Server.Implementations.Data
{
var val = string.Join(",", queryTopParentIds.Select(i => "'" + i + "'").ToArray());
- if (enableItemsByName)
+ if (enableItemsByName && includedItemByNameTypes.Count == 1)
{
- whereClauses.Add("(IsItemByName=@IsItemByName or TopParentId in (" + val + "))");
+ whereClauses.Add("(Type=@IncludedItemByNameType or TopParentId in (" + val + "))");
if (statement != null)
{
- statement.TryBind("@IsItemByName", true);
+ statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]);
}
}
+ else if (enableItemsByName && includedItemByNameTypes.Count > 1)
+ {
+ var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'").ToArray());
+ whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))");
+ }
else
{
whereClauses.Add("(TopParentId in (" + val + "))");
@@ -4573,29 +4521,57 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(string.Format("(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))", inClause));
}
- var excludeTagIndex = 0;
- foreach (var excludeTag in query.ExcludeTags)
+ if (query.ExcludeInheritedTags.Length > 0)
{
- whereClauses.Add("(Tags is null OR Tags not like @excludeTag" + excludeTagIndex + ")");
- if (statement != null)
- {
- statement.TryBind("@excludeTag" + excludeTagIndex, "%" + excludeTag + "%");
- }
- excludeTagIndex++;
+ var tagValues = query.ExcludeInheritedTags.Select(i => "'" + GetCleanValue(i) + "'").ToArray();
+ var tagValuesList = string.Join(",", tagValues);
+
+ whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + tagValuesList + ")) is null)");
}
- excludeTagIndex = 0;
- foreach (var excludeTag in query.ExcludeInheritedTags)
+ return whereClauses;
+ }
+
+ private List<string> GetItemByNameTypesInQuery(InternalItemsQuery query)
+ {
+ var list = new List<string>();
+
+ if (IsTypeInQuery(typeof(Person).Name, query))
{
- whereClauses.Add("(InheritedTags is null OR InheritedTags not like @excludeInheritedTag" + excludeTagIndex + ")");
- if (statement != null)
- {
- statement.TryBind("@excludeInheritedTag" + excludeTagIndex, "%" + excludeTag + "%");
- }
- excludeTagIndex++;
+ list.Add(typeof(Person).Name);
+ }
+ if (IsTypeInQuery(typeof(Genre).Name, query))
+ {
+ list.Add(typeof(Genre).Name);
+ }
+ if (IsTypeInQuery(typeof(MusicGenre).Name, query))
+ {
+ list.Add(typeof(MusicGenre).Name);
+ }
+ if (IsTypeInQuery(typeof(GameGenre).Name, query))
+ {
+ list.Add(typeof(GameGenre).Name);
+ }
+ if (IsTypeInQuery(typeof(MusicArtist).Name, query))
+ {
+ list.Add(typeof(MusicArtist).Name);
+ }
+ if (IsTypeInQuery(typeof(Studio).Name, query))
+ {
+ list.Add(typeof(Studio).Name);
}
- return whereClauses;
+ return list;
+ }
+
+ private bool IsTypeInQuery(string type, InternalItemsQuery query)
+ {
+ if (query.ExcludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase);
}
private string GetCleanValue(string value)
@@ -4615,6 +4591,11 @@ namespace Emby.Server.Implementations.Data
return false;
}
+ if (query.GroupBySeriesPresentationUniqueKey)
+ {
+ return false;
+ }
+
if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
{
return false;
@@ -4693,41 +4674,65 @@ namespace Emby.Server.Implementations.Data
private async Task UpdateInheritedTags(CancellationToken cancellationToken)
{
- var newValues = new List<Tuple<Guid, string>>();
+ var newValues = new List<Tuple<Guid, string[]>>();
- var commandText = "select Guid,InheritedTags,(select group_concat(Tags, '|') from TypedBaseItems where (guid=outer.guid) OR (guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid))) as NewInheritedTags from typedbaseitems as Outer where NewInheritedTags <> InheritedTags";
+ var commandText = @"select guid,
+(select group_concat(Value, '|') from ItemValues where (ItemValues.ItemId = Outer.Guid OR ItemValues.ItemId in ((Select AncestorId from AncestorIds where AncestorIds.ItemId=Outer.guid))) and ItemValues.Type = 4) NewInheritedTags,
+(select group_concat(Value, '|') from ItemValues where ItemValues.ItemId = Outer.Guid and ItemValues.Type = 6) CurrentInheritedTags
+from typedbaseitems as Outer
+where (NewInheritedTags <> CurrentInheritedTags or (NewInheritedTags is null) <> (CurrentInheritedTags is null))
+limit 100";
using (WriteLock.Write())
{
using (var connection = CreateConnection())
{
- foreach (var row in connection.Query(commandText))
+ connection.RunInTransaction(db =>
{
- var id = row.GetGuid(0);
- string value = row.IsDBNull(2) ? null : row.GetString(2);
+ foreach (var row in connection.Query(commandText))
+ {
+ var id = row.GetGuid(0);
+ string value = row.IsDBNull(1) ? null : row.GetString(1);
- newValues.Add(new Tuple<Guid, string>(id, value));
- }
+ var valuesArray = string.IsNullOrWhiteSpace(value) ? new string[] { } : value.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- Logger.Debug("UpdateInheritedTags - {0} rows", newValues.Count);
- if (newValues.Count == 0)
- {
- return;
- }
+ newValues.Add(new Tuple<Guid, string[]>(id, valuesArray));
+ }
- // write lock here
- using (var statement = PrepareStatement(connection, "Update TypedBaseItems set InheritedTags=@InheritedTags where Guid=@Guid"))
- {
- foreach (var item in newValues)
+ Logger.Debug("UpdateInheritedTags - {0} rows", newValues.Count);
+ if (newValues.Count == 0)
{
- var paramList = new List<object>();
+ return;
+ }
+
+ using (var insertStatement = PrepareStatement(connection, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, 6, @Value, @CleanValue)"))
+ {
+ using (var deleteStatement = PrepareStatement(connection, "delete from ItemValues where ItemId=@ItemId and Type=6"))
+ {
+ foreach (var item in newValues)
+ {
+ var guidBlob = item.Item1.ToGuidBlob();
- paramList.Add(item.Item1);
- paramList.Add(item.Item2);
+ deleteStatement.Reset();
+ deleteStatement.TryBind("@ItemId", guidBlob);
+ deleteStatement.MoveNext();
- statement.Execute(paramList.ToArray());
+ foreach (var itemValue in item.Item2)
+ {
+ insertStatement.Reset();
+
+ insertStatement.TryBind("@ItemId", guidBlob);
+ insertStatement.TryBind("@Value", itemValue);
+
+ insertStatement.TryBind("@CleanValue", GetCleanValue(itemValue));
+
+ insertStatement.MoveNext();
+ }
+ }
+ }
}
- }
+
+ }, TransactionMode);
}
}
}
@@ -5129,9 +5134,9 @@ namespace Emby.Server.Implementations.Data
var itemCountColumns = new List<Tuple<string, string>>();
- var typesToCount = query.IncludeItemTypes.ToList();
+ var typesToCount = query.IncludeItemTypes;
- if (typesToCount.Count > 0)
+ if (typesToCount.Length > 0)
{
var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B");
@@ -5191,7 +5196,7 @@ namespace Emby.Server.Implementations.Data
var whereText = " where Type=@SelectType";
- if (typesToCount.Count == 0)
+ if (typesToCount.Length == 0)
{
whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
}
@@ -5269,8 +5274,7 @@ namespace Emby.Server.Implementations.Data
var list = new List<Tuple<BaseItem, ItemCounts>>();
var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
- var statements = PrepareAllSafe(db, statementTexts)
- .ToList();
+ var statements = PrepareAllSafe(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -5345,7 +5349,7 @@ namespace Emby.Server.Implementations.Data
{
result.TotalRecordCount = list.Count;
}
- result.Items = list.ToArray();
+ result.Items = list.ToArray(list.Count);
return result;
@@ -5354,11 +5358,11 @@ namespace Emby.Server.Implementations.Data
}
}
- private ItemCounts GetItemCounts(IReadOnlyList<IResultSetValue> reader, int countStartColumn, List<string> typesToCount)
+ private ItemCounts GetItemCounts(IReadOnlyList<IResultSetValue> reader, int countStartColumn, string[] typesToCount)
{
var counts = new ItemCounts();
- if (typesToCount.Count == 0)
+ if (typesToCount.Length == 0)
{
return counts;
}
@@ -5416,7 +5420,7 @@ namespace Emby.Server.Implementations.Data
return counts;
}
- private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item)
+ private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
{
var list = new List<Tuple<int, string>>();
@@ -5435,7 +5439,10 @@ namespace Emby.Server.Implementations.Data
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.Keywords.Select(i => new Tuple<int, string>(5, i)));
+
+ // keywords was 5
+
+ list.AddRange(inheritedTags.Select(i => new Tuple<int, string>(6, i)));
return list;
}
@@ -5454,8 +5461,10 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
+ var guidBlob = itemId.ToGuidBlob();
+
// First delete
- db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidBlob());
+ db.Execute("delete from ItemValues where ItemId=@Id", guidBlob);
using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
{
@@ -5471,7 +5480,7 @@ namespace Emby.Server.Implementations.Data
statement.Reset();
- statement.TryBind("@ItemId", itemId.ToGuidBlob());
+ statement.TryBind("@ItemId", guidBlob);
statement.TryBind("@Type", pair.Item1);
statement.TryBind("@Value", itemValue);
@@ -5563,7 +5572,7 @@ namespace Emby.Server.Implementations.Data
return item;
}
- public IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query)
+ public List<MediaStream> GetMediaStreams(MediaStreamQuery query)
{
CheckDisposed();
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 2933a9f22..e1d0a1858 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -359,11 +359,11 @@ namespace Emby.Server.Implementations.Dto
{
if (user == null)
{
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList();
+ dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true);
}
else
{
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user).ToList();
+ dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user);
}
}
}
@@ -517,7 +517,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (!(item is LiveTvProgram) || fields.Contains(ItemFields.PlayAccess))
+ if (/*!(item is LiveTvProgram) ||*/ fields.Contains(ItemFields.PlayAccess))
{
dto.PlayAccess = item.GetPlayAccess(user);
}
@@ -639,7 +639,6 @@ namespace Emby.Server.Implementations.Dto
private void SetGameProperties(BaseItemDto dto, Game item)
{
- dto.Players = item.PlayersSupported;
dto.GameSystem = item.GameSystem;
dto.MultiPartGameFiles = item.MultiPartGameFiles;
}
@@ -649,12 +648,12 @@ namespace Emby.Server.Implementations.Dto
dto.GameSystem = item.GameSystemName;
}
- private List<string> GetImageTags(BaseItem item, List<ItemImageInfo> images)
+ private string[] GetImageTags(BaseItem item, List<ItemImageInfo> images)
{
return images
.Select(p => GetImageCacheTag(item, p))
.Where(i => i != null)
- .ToList();
+ .ToArray();
}
private string GetImageCacheTag(BaseItem item, ImageType type)
@@ -766,7 +765,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- dto.People = list.ToArray();
+ dto.People = list.ToArray(list.Count);
}
/// <summary>
@@ -864,11 +863,6 @@ namespace Emby.Server.Implementations.Dto
dto.DateCreated = item.DateCreated;
}
- if (fields.Contains(ItemFields.DisplayMediaType))
- {
- dto.DisplayMediaType = item.DisplayMediaType;
- }
-
if (fields.Contains(ItemFields.Settings))
{
dto.LockedFields = item.LockedFields;
@@ -894,11 +888,6 @@ namespace Emby.Server.Implementations.Dto
dto.Tags = item.Tags;
}
- if (fields.Contains(ItemFields.Keywords))
- {
- dto.Keywords = item.Keywords;
- }
-
var hasAspectRatio = item as IHasAspectRatio;
if (hasAspectRatio != null)
{
@@ -1003,7 +992,7 @@ namespace Emby.Server.Implementations.Dto
{
dto.RemoteTrailers = hasTrailers != null ?
hasTrailers.RemoteTrailers :
- new List<MediaUrl>();
+ new MediaUrl[] {};
}
dto.Name = item.Name;
@@ -1059,12 +1048,12 @@ namespace Emby.Server.Implementations.Dto
{
if (!string.IsNullOrWhiteSpace(item.Tagline))
{
- dto.Taglines = new List<string> { item.Tagline };
+ dto.Taglines = new string[] { item.Tagline };
}
if (dto.Taglines == null)
{
- dto.Taglines = new List<string>();
+ dto.Taglines = new string[]{};
}
}
@@ -1130,8 +1119,7 @@ namespace Emby.Server.Implementations.Dto
// Include artists that are not in the database yet, e.g., just added via metadata editor
//var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
- dto.ArtistItems = new List<NameIdPair>();
- dto.ArtistItems.AddRange(hasArtist.Artists
+ dto.ArtistItems = hasArtist.Artists
//.Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
@@ -1156,7 +1144,7 @@ namespace Emby.Server.Implementations.Dto
return null;
- }).Where(i => i != null));
+ }).Where(i => i != null).ToArray();
}
var hasAlbumArtist = item as IHasAlbumArtist;
@@ -1182,8 +1170,7 @@ namespace Emby.Server.Implementations.Dto
// })
// .ToList();
- dto.AlbumArtists = new List<NameIdPair>();
- dto.AlbumArtists.AddRange(hasAlbumArtist.AlbumArtists
+ dto.AlbumArtists = hasAlbumArtist.AlbumArtists
//.Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
@@ -1208,7 +1195,7 @@ namespace Emby.Server.Implementations.Dto
return null;
- }).Where(i => i != null));
+ }).Where(i => i != null).ToArray();
}
// Add video info
@@ -1224,9 +1211,9 @@ namespace Emby.Server.Implementations.Dto
dto.HasSubtitles = video.HasSubtitles;
}
- if (video.AdditionalParts.Count != 0)
+ if (video.AdditionalParts.Length != 0)
{
- dto.PartCount = video.AdditionalParts.Count + 1;
+ dto.PartCount = video.AdditionalParts.Length + 1;
}
if (fields.Contains(ItemFields.MediaSourceCount))
@@ -1276,7 +1263,7 @@ namespace Emby.Server.Implementations.Dto
var hasSpecialFeatures = item as IHasSpecialFeatures;
if (hasSpecialFeatures != null)
{
- var specialFeatureCount = hasSpecialFeatures.SpecialFeatureIds.Count;
+ var specialFeatureCount = hasSpecialFeatures.SpecialFeatureIds.Length;
if (specialFeatureCount > 0)
{
@@ -1321,15 +1308,6 @@ namespace Emby.Server.Implementations.Dto
Series episodeSeries = null;
- if (fields.Contains(ItemFields.SeriesGenres))
- {
- episodeSeries = episodeSeries ?? episode.Series;
- if (episodeSeries != null)
- {
- dto.SeriesGenres = episodeSeries.Genres.ToList();
- }
- }
-
//if (fields.Contains(ItemFields.SeriesPrimaryImage))
{
episodeSeries = episodeSeries ?? episode.Series;
@@ -1345,27 +1323,6 @@ namespace Emby.Server.Implementations.Dto
if (episodeSeries != null)
{
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
- if (!string.IsNullOrWhiteSpace(dto.SeriesStudio))
- {
- try
- {
- var studio = _libraryManager.GetStudio(dto.SeriesStudio);
-
- if (studio != null)
- {
- dto.SeriesStudioInfo = new StudioDto
- {
- Name = dto.SeriesStudio,
- Id = studio.Id.ToString("N"),
- PrimaryImageTag = GetImageCacheTag(studio, ImageType.Primary)
- };
- }
- }
- catch (Exception ex)
- {
-
- }
- }
}
}
}
@@ -1440,9 +1397,9 @@ namespace Emby.Server.Implementations.Dto
if (fields.Contains(ItemFields.ProductionLocations))
{
- if (item.ProductionLocations.Count > 0 || item is Movie)
+ if (item.ProductionLocations.Length > 0 || item is Movie)
{
- dto.ProductionLocations = item.ProductionLocations.ToArray();
+ dto.ProductionLocations = item.ProductionLocations;
}
}
@@ -1509,7 +1466,9 @@ namespace Emby.Server.Implementations.Dto
BaseItem parent = null;
var isFirst = true;
- while (((!dto.HasLogo && logoLimit > 0) || (!dto.HasArtImage && artLimit > 0) || (!dto.HasThumb && thumbLimit > 0) || parent is Series) &&
+ var imageTags = dto.ImageTags;
+
+ while (((!(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && logoLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && artLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && thumbLimit > 0) || parent is Series) &&
(parent = parent ?? (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
{
if (parent == null)
@@ -1519,7 +1478,7 @@ namespace Emby.Server.Implementations.Dto
var allImages = parent.ImageInfos;
- if (logoLimit > 0 && !dto.HasLogo && dto.ParentLogoItemId == null)
+ if (logoLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && dto.ParentLogoItemId == null)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Logo);
@@ -1529,7 +1488,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentLogoImageTag = GetImageCacheTag(parent, image);
}
}
- if (artLimit > 0 && !dto.HasArtImage && dto.ParentArtItemId == null)
+ if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
@@ -1539,7 +1498,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentArtImageTag = GetImageCacheTag(parent, image);
}
}
- if (thumbLimit > 0 && !dto.HasThumb && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
+ if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
@@ -1549,7 +1508,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentThumbImageTag = GetImageCacheTag(parent, image);
}
}
- if (backdropLimit > 0 && !dto.HasBackdrop)
+ if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
{
var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();
@@ -1591,12 +1550,12 @@ namespace Emby.Server.Implementations.Dto
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
/// <returns>Task.</returns>
- public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item)
+ public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasMetadata item)
{
dto.PrimaryImageAspectRatio = GetPrimaryImageAspectRatio(item);
}
- public double? GetPrimaryImageAspectRatio(IHasImages item)
+ public double? GetPrimaryImageAspectRatio(IHasMetadata item)
{
var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 1b0cbb936..38f51919a 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -29,6 +30,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@@ -40,6 +42,8 @@
<Compile Include="AppBase\BaseApplicationPaths.cs" />
<Compile Include="AppBase\BaseConfigurationManager.cs" />
<Compile Include="AppBase\ConfigurationHelper.cs" />
+ <Compile Include="ApplicationHost.cs" />
+ <Compile Include="ApplicationPathHelper.cs" />
<Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Browser\BrowserLauncher.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" />
@@ -52,9 +56,26 @@
<Compile Include="Collections\CollectionManager.cs" />
<Compile Include="Collections\CollectionsDynamicFolder.cs" />
<Compile Include="Configuration\ServerConfigurationManager.cs" />
+ <Compile Include="Cryptography\ASN1.cs" />
+ <Compile Include="Cryptography\ASN1Convert.cs" />
+ <Compile Include="Cryptography\BitConverterLE.cs" />
+ <Compile Include="Cryptography\CertificateGenerator.cs" />
+ <Compile Include="Cryptography\CryptoConvert.cs" />
+ <Compile Include="Cryptography\PfxGenerator.cs" />
+ <Compile Include="Cryptography\PKCS1.cs" />
+ <Compile Include="Cryptography\PKCS12.cs" />
+ <Compile Include="Cryptography\PKCS7.cs" />
+ <Compile Include="Cryptography\PKCS8.cs" />
+ <Compile Include="Cryptography\X501Name.cs" />
+ <Compile Include="Cryptography\X509Builder.cs" />
+ <Compile Include="Cryptography\X509Certificate.cs" />
+ <Compile Include="Cryptography\X509CertificateBuilder.cs" />
+ <Compile Include="Cryptography\X509CertificateCollection.cs" />
+ <Compile Include="Cryptography\X509Extension.cs" />
+ <Compile Include="Cryptography\X509Extensions.cs" />
+ <Compile Include="Cryptography\X520Attributes.cs" />
<Compile Include="Data\ManagedConnection.cs" />
<Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
- <Compile Include="Data\SqliteFileOrganizationRepository.cs" />
<Compile Include="Data\SqliteItemRepository.cs" />
<Compile Include="Data\SqliteUserDataRepository.cs" />
<Compile Include="Data\SqliteUserRepository.cs" />
@@ -64,6 +85,7 @@
<Compile Include="Devices\DeviceRepository.cs" />
<Compile Include="Dto\DtoService.cs" />
<Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
+ <Compile Include="EntryPoints\ExternalPortForwarding.cs" />
<Compile Include="EntryPoints\KeepServerAwake.cs" />
<Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
<Compile Include="EntryPoints\LoadRegistrations.cs" />
@@ -79,13 +101,7 @@
<Compile Include="FFMpeg\FFMpegInfo.cs" />
<Compile Include="FFMpeg\FFMpegInstallInfo.cs" />
<Compile Include="FFMpeg\FFMpegLoader.cs" />
- <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
- <Compile Include="FileOrganization\Extensions.cs" />
- <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
- <Compile Include="FileOrganization\FileOrganizationService.cs" />
- <Compile Include="FileOrganization\NameUtils.cs" />
- <Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
- <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
+ <Compile Include="HttpServerFactory.cs" />
<Compile Include="HttpServer\FileWriter.cs" />
<Compile Include="HttpServer\HttpListenerHost.cs" />
<Compile Include="HttpServer\HttpResultFactory.cs" />
@@ -107,7 +123,9 @@
<Compile Include="Images\BaseDynamicImageProvider.cs" />
<Compile Include="IO\AsyncStreamCopier.cs" />
<Compile Include="IO\FileRefresher.cs" />
+ <Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="IO\MbLinkShortcutHandler.cs" />
+ <Compile Include="IO\MemoryStreamProvider.cs" />
<Compile Include="IO\ThrottledStream.cs" />
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
<Compile Include="Library\LibraryManager.cs" />
@@ -178,6 +196,8 @@
<Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
<Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
+ <Compile Include="Localization\TextLocalizer.cs" />
+ <Compile Include="Logging\ConsoleLogger.cs" />
<Compile Include="Logging\UnhandledExceptionWriter.cs" />
<Compile Include="MediaEncoder\EncodingManager.cs" />
<Compile Include="Migrations\IVersionMigration.cs" />
@@ -229,7 +249,6 @@
<Compile Include="Social\SharingManager.cs" />
<Compile Include="Social\SharingRepository.cs" />
<Compile Include="Sorting\AiredEpisodeOrderComparer.cs" />
- <Compile Include="Sorting\AirTimeComparer.cs" />
<Compile Include="Sorting\AlbumArtistComparer.cs" />
<Compile Include="Sorting\AlbumComparer.cs" />
<Compile Include="Sorting\AlphanumComparator.cs" />
@@ -257,6 +276,7 @@
<Compile Include="Sorting\StartDateComparer.cs" />
<Compile Include="Sorting\StudioComparer.cs" />
<Compile Include="StartupOptions.cs" />
+ <Compile Include="SystemEvents.cs" />
<Compile Include="TV\SeriesPostScanTask.cs" />
<Compile Include="TV\TVSeriesManager.cs" />
<Compile Include="Udp\UdpServer.cs" />
@@ -268,6 +288,26 @@
<EmbeddedResource Include="Localization\iso6392.txt" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\Emby.Common.Implementations\Emby.Common.Implementations.csproj">
+ <Project>{1e37a338-9f57-4b70-bd6d-bb9c591e319b}</Project>
+ <Name>Emby.Common.Implementations</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj">
+ <Project>{805844ab-e92f-45e6-9d99-4f6d48d129a5}</Project>
+ <Name>Emby.Dlna</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj">
+ <Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project>
+ <Name>Emby.Drawing</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj">
+ <Project>{89ab4548-770d-41fd-a891-8daff44f452c}</Project>
+ <Name>Emby.Photos</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj">
+ <Project>{4fd51ac5-2c16-4308-a993-c3a84f3b4582}</Project>
+ <Name>MediaBrowser.Api</Name>
+ </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
@@ -276,6 +316,10 @@
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
+ <Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project>
+ <Name>MediaBrowser.LocalMetadata</Name>
+ </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
@@ -288,10 +332,29 @@
<Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj">
+ <Project>{5624b7b5-b5a7-41d8-9f10-cc5611109619}</Project>
+ <Name>MediaBrowser.WebDashboard</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj">
+ <Project>{23499896-b135-4527-8574-c26e926ea99e}</Project>
+ <Name>MediaBrowser.XbmcMetadata</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj">
+ <Project>{cb7f2326-6497-4a3d-ba03-48513b17a7be}</Project>
+ <Name>Mono.Nat</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
+ <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
+ <Name>OpenSubtitlesHandler</Name>
+ </ProjectReference>
<ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj">
<Project>{1d74413b-e7cf-455b-b021-f52bdf881542}</Project>
<Name>SocketHttpListener</Name>
</ProjectReference>
+ <Reference Include="Emby.Server.MediaEncoding">
+ <HintPath>..\ThirdParty\emby\Emby.Server.MediaEncoding.dll</HintPath>
+ </Reference>
<Reference Include="Emby.XmlTv, Version=1.0.6387.29335, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Emby.XmlTv.1.0.9\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
<Private>True</Private>
@@ -300,6 +363,16 @@
<HintPath>..\packages\MediaBrowser.Naming.1.0.5\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath>
<Private>True</Private>
</Reference>
+ <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
+ </Reference>
+ <Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>
+ </Reference>
<Reference Include="SQLitePCL.pretty, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCL.pretty.1.1.0\lib\portable-net45+netcore45+wpa81+wp8\SQLitePCL.pretty.dll</HintPath>
<Private>True</Private>
@@ -307,10 +380,10 @@
</ItemGroup>
<ItemGroup>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="System" />
+ <Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
@@ -421,6 +494,9 @@
<ItemGroup>
<EmbeddedResource Include="Localization\Ratings\es.txt" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\ro.txt" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 2c7e6a487..c96799b2f 100644
--- a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Net;
+using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -11,9 +12,9 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Threading;
using Mono.Nat;
-using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
-namespace Emby.Server.Core.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
public class ExternalPortForwarding : IServerEntryPoint
{
@@ -50,7 +51,7 @@ namespace Emby.Server.Core.EntryPoints
values.Add(config.EnableHttps.ToString());
values.Add(_appHost.EnableHttps.ToString());
- return string.Join("|", values.ToArray());
+ return string.Join("|", values.ToArray(values.Count));
}
void _config_ConfigurationUpdated(object sender, EventArgs e)
diff --git a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
index 9fbe06673..99d39ffe0 100644
--- a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -58,7 +59,7 @@ namespace Emby.Server.Implementations.EntryPoints
session.ApplicationVersion
};
- var key = string.Join("_", keys.ToArray()).GetMD5();
+ var key = string.Join("_", keys.ToArray(keys.Count)).GetMD5();
_apps.GetOrAdd(key, guid => GetNewClientInfo(session));
}
diff --git a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
deleted file mode 100644
index cf9fdbb16..000000000
--- a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ /dev/null
@@ -1,813 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.Library;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Naming.TV;
-using EpisodeInfo = MediaBrowser.Controller.Providers.EpisodeInfo;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public class EpisodeFileOrganizer
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IFileOrganizationService _organizationService;
- private readonly IServerConfigurationManager _config;
- private readonly IProviderManager _providerManager;
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager)
- {
- _organizationService = organizationService;
- _config = config;
- _fileSystem = fileSystem;
- _logger = logger;
- _libraryManager = libraryManager;
- _libraryMonitor = libraryMonitor;
- _providerManager = providerManager;
- }
-
- public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken)
- {
- _logger.Info("Sorting file {0}", path);
-
- var result = new FileOrganizationResult
- {
- Date = DateTime.UtcNow,
- OriginalPath = path,
- OriginalFileName = Path.GetFileName(path),
- Type = FileOrganizerType.Episode,
- FileSize = _fileSystem.GetFileInfo(path).Length
- };
-
- try
- {
- if (_libraryMonitor.IsPathLocked(path))
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = "Path is locked by other processes. Please try again later.";
- return result;
- }
-
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var resolver = new EpisodeResolver(namingOptions, new NullLogger());
-
- var episodeInfo = resolver.Resolve(path, false) ??
- new MediaBrowser.Naming.TV.EpisodeInfo();
-
- var seriesName = episodeInfo.SeriesName;
-
- if (!string.IsNullOrEmpty(seriesName))
- {
- var seasonNumber = episodeInfo.SeasonNumber;
-
- result.ExtractedSeasonNumber = seasonNumber;
-
- // Passing in true will include a few extra regex's
- var episodeNumber = episodeInfo.EpisodeNumber;
-
- result.ExtractedEpisodeNumber = episodeNumber;
-
- var premiereDate = episodeInfo.IsByDate ?
- new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
- (DateTime?)null;
-
- if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
- {
- if (episodeInfo.IsByDate)
- {
- _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
- }
- else
- {
- _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
- }
-
- var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
-
- result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
-
- await OrganizeEpisode(path,
- seriesName,
- seasonNumber,
- episodeNumber,
- endingEpisodeNumber,
- premiereDate,
- options,
- overwriteExisting,
- false,
- result,
- cancellationToken).ConfigureAwait(false);
- }
- else
- {
- var msg = string.Format("Unable to determine episode number from {0}", path);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- }
- }
- else
- {
- var msg = string.Format("Unable to determine series name from {0}", path);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- }
-
- var previousResult = _organizationService.GetResultBySourcePath(path);
-
- if (previousResult != null)
- {
- // Don't keep saving the same result over and over if nothing has changed
- if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
- {
- return previousResult;
- }
- }
-
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- }
-
- return result;
- }
-
- public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, AutoOrganizeOptions options, CancellationToken cancellationToken)
- {
- var result = _organizationService.GetResult(request.ResultId);
-
- try
- {
- Series series = null;
-
- if (request.NewSeriesProviderIds.Count > 0)
- {
- // We're having a new series here
- SeriesInfo seriesRequest = new SeriesInfo();
- seriesRequest.ProviderIds = request.NewSeriesProviderIds;
-
- var refreshOptions = new MetadataRefreshOptions(_fileSystem);
- series = new Series();
- series.Id = Guid.NewGuid();
- series.Name = request.NewSeriesName;
-
- int year;
- if (int.TryParse(request.NewSeriesYear, out year))
- {
- series.ProductionYear = year;
- }
-
- var seriesFolderName = series.Name;
- if (series.ProductionYear.HasValue)
- {
- seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
- }
-
- seriesFolderName = _fileSystem.GetValidFilename(seriesFolderName);
-
- series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
-
- series.ProviderIds = request.NewSeriesProviderIds;
-
- await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
-
- if (series == null)
- {
- // Existing Series
- series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
- }
-
- await OrganizeEpisode(result.OriginalPath,
- series,
- request.SeasonNumber,
- request.EpisodeNumber,
- request.EndingEpisodeNumber,
- null,
- options,
- true,
- request.RememberCorrection,
- result,
- cancellationToken).ConfigureAwait(false);
-
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- }
-
- return result;
- }
-
- private Task OrganizeEpisode(string sourcePath,
- string seriesName,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpiosdeNumber,
- DateTime? premiereDate,
- AutoOrganizeOptions options,
- bool overwriteExisting,
- bool rememberCorrection,
- FileOrganizationResult result,
- CancellationToken cancellationToken)
- {
- var series = GetMatchingSeries(seriesName, result, options);
-
- if (series == null)
- {
- var msg = string.Format("Unable to find series in library matching name {0}", seriesName);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- return Task.FromResult(true);
- }
-
- return OrganizeEpisode(sourcePath,
- series,
- seasonNumber,
- episodeNumber,
- endingEpiosdeNumber,
- premiereDate,
- options,
- overwriteExisting,
- rememberCorrection,
- result,
- cancellationToken);
- }
-
- private async Task OrganizeEpisode(string sourcePath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpiosdeNumber,
- DateTime? premiereDate,
- AutoOrganizeOptions options,
- bool overwriteExisting,
- bool rememberCorrection,
- FileOrganizationResult result,
- CancellationToken cancellationToken)
- {
- _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
-
- var originalExtractedSeriesString = result.ExtractedName;
-
- bool isNew = string.IsNullOrWhiteSpace(result.Id);
-
- if (isNew)
- {
- await _organizationService.SaveResult(result, cancellationToken);
- }
-
- if (!_organizationService.AddToInProgressList(result, isNew))
- {
- throw new Exception("File is currently processed otherwise. Please try again later.");
- }
-
- try
- {
- // Proceed to sort the file
- var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
-
- if (string.IsNullOrEmpty(newPath))
- {
- var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
- throw new Exception(msg);
- }
-
- _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
- result.TargetPath = newPath;
-
- var fileExists = _fileSystem.FileExists(result.TargetPath);
- var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
-
- if (!overwriteExisting)
- {
- if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
- {
- var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- return;
- }
-
- if (fileExists)
- {
- var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- result.TargetPath = newPath;
- return;
- }
-
- if (otherDuplicatePaths.Count > 0)
- {
- var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- result.DuplicatePaths = otherDuplicatePaths;
- return;
- }
- }
-
- PerformFileSorting(options.TvOptions, result);
-
- if (overwriteExisting)
- {
- var hasRenamedFiles = false;
-
- foreach (var path in otherDuplicatePaths)
- {
- _logger.Debug("Removing duplicate episode {0}", path);
-
- _libraryMonitor.ReportFileSystemChangeBeginning(path);
-
- var renameRelatedFiles = !hasRenamedFiles &&
- string.Equals(_fileSystem.GetDirectoryName(path), _fileSystem.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
-
- if (renameRelatedFiles)
- {
- hasRenamedFiles = true;
- }
-
- try
- {
- DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error removing duplicate episode", ex, path);
- }
- finally
- {
- _libraryMonitor.ReportFileSystemChangeComplete(path, true);
- }
- }
- }
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- _logger.Warn(ex.Message);
- return;
- }
- finally
- {
- _organizationService.RemoveFromInprogressList(result);
- }
-
- if (rememberCorrection)
- {
- SaveSmartMatchString(originalExtractedSeriesString, series, options);
- }
- }
-
- private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options)
- {
- if (string.IsNullOrEmpty(matchString) || matchString.Length < 3)
- {
- return;
- }
-
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, series.Name, StringComparison.OrdinalIgnoreCase));
-
- if (info == null)
- {
- info = new SmartMatchInfo();
- info.ItemName = series.Name;
- info.OrganizerType = FileOrganizerType.Episode;
- info.DisplayName = series.Name;
- var list = options.SmartMatchInfos.ToList();
- list.Add(info);
- options.SmartMatchInfos = list.ToArray();
- }
-
- if (!info.MatchStrings.Contains(matchString, StringComparer.OrdinalIgnoreCase))
- {
- var list = info.MatchStrings.ToList();
- list.Add(matchString);
- info.MatchStrings = list.ToArray();
- _config.SaveAutoOrganizeOptions(options);
- }
- }
-
- private void DeleteLibraryFile(string path, bool renameRelatedFiles, string targetPath)
- {
- _fileSystem.DeleteFile(path);
-
- if (!renameRelatedFiles)
- {
- return;
- }
-
- // Now find other files
- var originalFilenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
- var directory = _fileSystem.GetDirectoryName(path);
-
- if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory))
- {
- // Get all related files, e.g. metadata, images, etc
- var files = _fileSystem.GetFilePaths(directory)
- .Where(i => (Path.GetFileNameWithoutExtension(i) ?? string.Empty).StartsWith(originalFilenameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- var targetFilenameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
-
- foreach (var file in files)
- {
- directory = _fileSystem.GetDirectoryName(file);
- var filename = Path.GetFileName(file);
-
- filename = filename.Replace(originalFilenameWithoutExtension, targetFilenameWithoutExtension,
- StringComparison.OrdinalIgnoreCase);
-
- var destination = Path.Combine(directory, filename);
-
- _fileSystem.MoveFile(file, destination);
- }
- }
- }
-
- private List<string> GetOtherDuplicatePaths(string targetPath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpisodeNumber)
- {
- // TODO: Support date-naming?
- if (!seasonNumber.HasValue || !episodeNumber.HasValue)
- {
- return new List<string>();
- }
-
- var episodePaths = series.GetRecursiveChildren(i => i is Episode)
- .OfType<Episode>()
- .Where(i =>
- {
- var locationType = i.LocationType;
-
- // Must be file system based and match exactly
- if (locationType != LocationType.Remote &&
- locationType != LocationType.Virtual &&
- i.ParentIndexNumber.HasValue &&
- i.ParentIndexNumber.Value == seasonNumber &&
- i.IndexNumber.HasValue &&
- i.IndexNumber.Value == episodeNumber)
- {
-
- if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue)
- {
- return endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue &&
- endingEpisodeNumber.Value == i.IndexNumberEnd.Value;
- }
-
- return true;
- }
-
- return false;
- })
- .Select(i => i.Path)
- .ToList();
-
- var folder = _fileSystem.GetDirectoryName(targetPath);
- var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath);
-
- try
- {
- var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder)
- .Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));
-
- episodePaths.AddRange(filesOfOtherExtensions);
- }
- catch (IOException)
- {
- // No big deal. Maybe the season folder doesn't already exist.
- }
-
- return episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
- }
-
- private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result)
- {
- // We should probably handle this earlier so that we never even make it this far
- if (string.Equals(result.OriginalPath, result.TargetPath, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
-
- _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
-
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(result.TargetPath));
-
- var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath);
-
- try
- {
- if (targetAlreadyExists || options.CopyOriginalFile)
- {
- _fileSystem.CopyFile(result.OriginalPath, result.TargetPath, true);
- }
- else
- {
- _fileSystem.MoveFile(result.OriginalPath, result.TargetPath);
- }
-
- result.Status = FileSortingStatus.Success;
- result.StatusMessage = string.Empty;
- }
- catch (Exception ex)
- {
- var errorMsg = string.Format("Failed to move file from {0} to {1}: {2}", result.OriginalPath, result.TargetPath, ex.Message);
-
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = errorMsg;
- _logger.ErrorException(errorMsg, ex);
-
- return;
- }
- finally
- {
- _libraryMonitor.ReportFileSystemChangeComplete(result.TargetPath, true);
- }
-
- if (targetAlreadyExists && !options.CopyOriginalFile)
- {
- try
- {
- _fileSystem.DeleteFile(result.OriginalPath);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
- }
- }
- }
-
- private Series GetMatchingSeries(string seriesName, FileOrganizationResult result, AutoOrganizeOptions options)
- {
- var parsedName = _libraryManager.ParseName(seriesName);
-
- var yearInName = parsedName.Year;
- var nameWithoutYear = parsedName.Name;
-
- result.ExtractedName = nameWithoutYear;
- result.ExtractedYear = yearInName;
-
- var series = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- DtoOptions = new DtoOptions(true)
- })
- .Cast<Series>()
- .Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
- .Where(i => i.Item2 > 0)
- .OrderByDescending(i => i.Item2)
- .Select(i => i.Item1)
- .FirstOrDefault();
-
- if (series == null)
- {
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(e => e.MatchStrings.Contains(nameWithoutYear, StringComparer.OrdinalIgnoreCase));
-
- if (info != null)
- {
- series = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- Name = info.ItemName,
- DtoOptions = new DtoOptions(true)
-
- }).Cast<Series>().FirstOrDefault();
- }
- }
-
- return series;
- }
-
- /// <summary>
- /// Gets the new path.
- /// </summary>
- /// <param name="sourcePath">The source path.</param>
- /// <param name="series">The series.</param>
- /// <param name="seasonNumber">The season number.</param>
- /// <param name="episodeNumber">The episode number.</param>
- /// <param name="endingEpisodeNumber">The ending episode number.</param>
- /// <param name="premiereDate">The premiere date.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>System.String.</returns>
- private async Task<string> GetNewPath(string sourcePath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpisodeNumber,
- DateTime? premiereDate,
- TvFileOrganizationOptions options,
- CancellationToken cancellationToken)
- {
- var episodeInfo = new EpisodeInfo
- {
- IndexNumber = episodeNumber,
- IndexNumberEnd = endingEpisodeNumber,
- MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
- MetadataLanguage = series.GetPreferredMetadataLanguage(),
- ParentIndexNumber = seasonNumber,
- SeriesProviderIds = series.ProviderIds,
- PremiereDate = premiereDate
- };
-
- var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
- {
- SearchInfo = episodeInfo
-
- }, cancellationToken).ConfigureAwait(false);
-
- var episode = searchResults.FirstOrDefault();
-
- if (episode == null)
- {
- var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
- _logger.Warn(msg);
- throw new Exception(msg);
- }
-
- var episodeName = episode.Name;
-
- //if (string.IsNullOrWhiteSpace(episodeName))
- //{
- // var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
- // _logger.Warn(msg);
- // return null;
- //}
-
- seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
- episodeNumber = episodeNumber ?? episode.IndexNumber;
-
- var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
-
- var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options);
-
- if (string.IsNullOrEmpty(episodeFileName))
- {
- // cause failure
- return string.Empty;
- }
-
- newPath = Path.Combine(newPath, episodeFileName);
-
- return newPath;
- }
-
- /// <summary>
- /// Gets the season folder path.
- /// </summary>
- /// <param name="series">The series.</param>
- /// <param name="seasonNumber">The season number.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- private string GetSeasonFolderPath(Series series, int seasonNumber, TvFileOrganizationOptions options)
- {
- // If there's already a season folder, use that
- var season = series
- .GetRecursiveChildren(i => i is Season && i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber)
- .FirstOrDefault();
-
- if (season != null)
- {
- return season.Path;
- }
-
- var path = series.Path;
-
- if (series.ContainsEpisodesWithoutSeasonFolders)
- {
- return path;
- }
-
- if (seasonNumber == 0)
- {
- return Path.Combine(path, _fileSystem.GetValidFilename(options.SeasonZeroFolderName));
- }
-
- var seasonFolderName = options.SeasonFolderPattern
- .Replace("%s", seasonNumber.ToString(_usCulture))
- .Replace("%0s", seasonNumber.ToString("00", _usCulture))
- .Replace("%00s", seasonNumber.ToString("000", _usCulture));
-
- return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName));
- }
-
- private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options)
- {
- seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
-
- if (string.IsNullOrWhiteSpace(episodeTitle))
- {
- episodeTitle = string.Empty;
- }
- else
- {
- episodeTitle = _fileSystem.GetValidFilename(episodeTitle).Trim();
- }
-
- var sourceExtension = (Path.GetExtension(sourcePath) ?? string.Empty).TrimStart('.');
-
- var pattern = endingEpisodeNumber.HasValue ? options.MultiEpisodeNamePattern : options.EpisodeNamePattern;
-
- if (string.IsNullOrWhiteSpace(pattern))
- {
- throw new Exception("GetEpisodeFileName: Configured episode name pattern is empty!");
- }
-
- var result = pattern.Replace("%sn", seriesName)
- .Replace("%s.n", seriesName.Replace(" ", "."))
- .Replace("%s_n", seriesName.Replace(" ", "_"))
- .Replace("%s", seasonNumber.ToString(_usCulture))
- .Replace("%0s", seasonNumber.ToString("00", _usCulture))
- .Replace("%00s", seasonNumber.ToString("000", _usCulture))
- .Replace("%ext", sourceExtension)
- .Replace("%en", "%#1")
- .Replace("%e.n", "%#2")
- .Replace("%e_n", "%#3");
-
- if (endingEpisodeNumber.HasValue)
- {
- result = result.Replace("%ed", endingEpisodeNumber.Value.ToString(_usCulture))
- .Replace("%0ed", endingEpisodeNumber.Value.ToString("00", _usCulture))
- .Replace("%00ed", endingEpisodeNumber.Value.ToString("000", _usCulture));
- }
-
- result = result.Replace("%e", episodeNumber.ToString(_usCulture))
- .Replace("%0e", episodeNumber.ToString("00", _usCulture))
- .Replace("%00e", episodeNumber.ToString("000", _usCulture));
-
- if (result.Contains("%#"))
- {
- result = result.Replace("%#1", episodeTitle)
- .Replace("%#2", episodeTitle.Replace(" ", "."))
- .Replace("%#3", episodeTitle.Replace(" ", "_"));
- }
-
- // Finally, call GetValidFilename again in case user customized the episode expression with any invalid filename characters
- return _fileSystem.GetValidFilename(result).Trim();
- }
-
- private bool IsSameEpisode(string sourcePath, string newPath)
- {
- try
- {
- var sourceFileInfo = _fileSystem.GetFileInfo(sourcePath);
- var destinationFileInfo = _fileSystem.GetFileInfo(newPath);
-
- if (sourceFileInfo.Length == destinationFileInfo.Length)
- {
- return true;
- }
- }
- catch (FileNotFoundException)
- {
- return false;
- }
- catch (IOException)
- {
- return false;
- }
-
- return false;
- }
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/Extensions.cs b/Emby.Server.Implementations/FileOrganization/Extensions.cs
deleted file mode 100644
index 506bc0327..000000000
--- a/Emby.Server.Implementations/FileOrganization/Extensions.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.FileOrganization;
-using System.Collections.Generic;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public static class ConfigurationExtension
- {
- public static AutoOrganizeOptions GetAutoOrganizeOptions(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<AutoOrganizeOptions>("autoorganize");
- }
- public static void SaveAutoOrganizeOptions(this IConfigurationManager manager, AutoOrganizeOptions options)
- {
- manager.SaveConfiguration("autoorganize", options);
- }
- }
-
- public class AutoOrganizeOptionsFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "autoorganize",
- ConfigurationType = typeof (AutoOrganizeOptions)
- }
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs b/Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
deleted file mode 100644
index 2a0176547..000000000
--- a/Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- /// <summary>
- /// Class SessionInfoWebSocketListener
- /// </summary>
- class FileOrganizationNotifier : IServerEntryPoint
- {
- private readonly IFileOrganizationService _organizationService;
- private readonly ISessionManager _sessionManager;
- private readonly ITaskManager _taskManager;
-
- public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager, ITaskManager taskManager)
- {
- _organizationService = organizationService;
- _sessionManager = sessionManager;
- _taskManager = taskManager;
- }
-
- public void Run()
- {
- _organizationService.ItemAdded += _organizationService_ItemAdded;
- _organizationService.ItemRemoved += _organizationService_ItemRemoved;
- _organizationService.ItemUpdated += _organizationService_ItemUpdated;
- _organizationService.LogReset += _organizationService_LogReset;
-
- //_taskManager.TaskCompleted += _taskManager_TaskCompleted;
- }
-
- private void _organizationService_LogReset(object sender, EventArgs e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_LogReset", (FileOrganizationResult)null, CancellationToken.None);
- }
-
- private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemUpdated", e.Argument, CancellationToken.None);
- }
-
- private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemRemoved", e.Argument, CancellationToken.None);
- }
-
- private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemAdded", e.Argument, CancellationToken.None);
- }
-
- //private void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- //{
- // var taskWithKey = e.Task.ScheduledTask as IHasKey;
- // if (taskWithKey != null && taskWithKey.Key == "AutoOrganize")
- // {
- // _sessionManager.SendMessageToAdminSessions("AutoOrganize_TaskCompleted", (FileOrganizationResult)null, CancellationToken.None);
- // }
- //}
-
- public void Dispose()
- {
- _organizationService.ItemAdded -= _organizationService_ItemAdded;
- _organizationService.ItemRemoved -= _organizationService_ItemRemoved;
- _organizationService.ItemUpdated -= _organizationService_ItemUpdated;
- _organizationService.LogReset -= _organizationService_LogReset;
-
- //_taskManager.TaskCompleted -= _taskManager_TaskCompleted;
- }
-
-
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs b/Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs
deleted file mode 100644
index d95bd8734..000000000
--- a/Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Common.Events;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public class FileOrganizationService : IFileOrganizationService
- {
- private readonly ITaskManager _taskManager;
- private readonly IFileOrganizationRepository _repo;
- private readonly ILogger _logger;
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IProviderManager _providerManager;
- private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>();
-
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
- public event EventHandler LogReset;
-
- public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
- {
- _taskManager = taskManager;
- _repo = repo;
- _logger = logger;
- _libraryMonitor = libraryMonitor;
- _libraryManager = libraryManager;
- _config = config;
- _fileSystem = fileSystem;
- _providerManager = providerManager;
- }
-
- public void BeginProcessNewFiles()
- {
- _taskManager.CancelIfRunningAndQueue<OrganizerScheduledTask>();
- }
-
- public Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
- {
- if (result == null || string.IsNullOrEmpty(result.OriginalPath))
- {
- throw new ArgumentNullException("result");
- }
-
- result.Id = result.OriginalPath.GetMD5().ToString("N");
-
- return _repo.SaveResult(result, cancellationToken);
- }
-
- public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
- {
- var results = _repo.GetResults(query);
-
- foreach (var result in results.Items)
- {
- result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
- }
-
- return results;
- }
-
- public FileOrganizationResult GetResult(string id)
- {
- var result = _repo.GetResult(id);
-
- if (result != null)
- {
- result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
- }
-
- return result;
- }
-
- public FileOrganizationResult GetResultBySourcePath(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var id = path.GetMD5().ToString("N");
-
- return GetResult(id);
- }
-
- public async Task DeleteOriginalFile(string resultId)
- {
- var result = _repo.GetResult(resultId);
-
- _logger.Info("Requested to delete {0}", result.OriginalPath);
-
- if (!AddToInProgressList(result, false))
- {
- throw new Exception("Path is currently processed otherwise. Please try again later.");
- }
-
- try
- {
- _fileSystem.DeleteFile(result.OriginalPath);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
- }
- finally
- {
- RemoveFromInprogressList(result);
- }
-
- await _repo.Delete(resultId);
-
- EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
-
- private AutoOrganizeOptions GetAutoOrganizeOptions()
- {
- return _config.GetAutoOrganizeOptions();
- }
-
- public async Task PerformOrganization(string resultId)
- {
- var result = _repo.GetResult(resultId);
-
- if (string.IsNullOrEmpty(result.TargetPath))
- {
- throw new ArgumentException("No target path available.");
- }
-
- var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- var organizeResult = await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
- .ConfigureAwait(false);
-
- if (organizeResult.Status != FileSortingStatus.Success)
- {
- throw new Exception(result.StatusMessage);
- }
- }
-
- public async Task ClearLog()
- {
- await _repo.DeleteAll();
- EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger);
- }
-
- public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
- {
- var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- var result = await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
-
- if (result.Status != FileSortingStatus.Success)
- {
- throw new Exception(result.StatusMessage);
- }
- }
-
- public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- var options = GetAutoOrganizeOptions();
-
- var items = options.SmartMatchInfos.Skip(query.StartIndex ?? 0).Take(query.Limit ?? Int32.MaxValue).ToArray();
-
- return new QueryResult<SmartMatchInfo>()
- {
- Items = items,
- TotalRecordCount = options.SmartMatchInfos.Length
- };
- }
-
- public void DeleteSmartMatchEntry(string itemName, string matchString)
- {
- if (string.IsNullOrEmpty(itemName))
- {
- throw new ArgumentNullException("itemName");
- }
-
- if (string.IsNullOrEmpty(matchString))
- {
- throw new ArgumentNullException("matchString");
- }
-
- var options = GetAutoOrganizeOptions();
-
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, itemName));
-
- if (info != null && info.MatchStrings.Contains(matchString))
- {
- var list = info.MatchStrings.ToList();
- list.Remove(matchString);
- info.MatchStrings = list.ToArray();
-
- if (info.MatchStrings.Length == 0)
- {
- var infos = options.SmartMatchInfos.ToList();
- infos.Remove(info);
- options.SmartMatchInfos = infos.ToArray();
- }
-
- _config.SaveAutoOrganizeOptions(options);
- }
- }
-
- /// <summary>
- /// Attempts to add a an item to the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
- /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
- public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem)
- {
- if (string.IsNullOrWhiteSpace(result.Id))
- {
- result.Id = result.OriginalPath.GetMD5().ToString("N");
- }
-
- if (!_inProgressItemIds.TryAdd(result.Id, false))
- {
- return false;
- }
-
- result.IsInProgress = true;
-
- if (isNewItem)
- {
- EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
- else
- {
- EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
-
- return true;
- }
-
- /// <summary>
- /// Removes an item from the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
- public bool RemoveFromInprogressList(FileOrganizationResult result)
- {
- bool itemValue;
- var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue);
-
- result.IsInProgress = false;
-
- EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
-
- return retval;
- }
-
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/NameUtils.cs b/Emby.Server.Implementations/FileOrganization/NameUtils.cs
deleted file mode 100644
index eb22ca4ea..000000000
--- a/Emby.Server.Implementations/FileOrganization/NameUtils.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Globalization;
-using MediaBrowser.Controller.Extensions;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public static class NameUtils
- {
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- internal static Tuple<T, int> GetMatchScore<T>(string sortedName, int? year, T series)
- where T : BaseItem
- {
- var score = 0;
-
- var seriesNameWithoutYear = series.Name;
- if (series.ProductionYear.HasValue)
- {
- seriesNameWithoutYear = seriesNameWithoutYear.Replace(series.ProductionYear.Value.ToString(UsCulture), String.Empty);
- }
-
- if (IsNameMatch(sortedName, seriesNameWithoutYear))
- {
- score++;
-
- if (year.HasValue && series.ProductionYear.HasValue)
- {
- if (year.Value == series.ProductionYear.Value)
- {
- score++;
- }
- else
- {
- // Regardless of name, return a 0 score if the years don't match
- return new Tuple<T, int>(series, 0);
- }
- }
- }
-
- return new Tuple<T, int>(series, score);
- }
-
-
- private static bool IsNameMatch(string name1, string name2)
- {
- name1 = GetComparableName(name1);
- name2 = GetComparableName(name2);
-
- return String.Equals(name1, name2, StringComparison.OrdinalIgnoreCase);
- }
-
- private static string GetComparableName(string name)
- {
- name = name.RemoveDiacritics();
-
- name = " " + name + " ";
-
- name = name.Replace(".", " ")
- .Replace("_", " ")
- .Replace(" and ", " ")
- .Replace(".and.", " ")
- .Replace("&", " ")
- .Replace("!", " ")
- .Replace("(", " ")
- .Replace(")", " ")
- .Replace(":", " ")
- .Replace(",", " ")
- .Replace("-", " ")
- .Replace("'", " ")
- .Replace("[", " ")
- .Replace("]", " ")
- .Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(" the ", String.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(" ", String.Empty);
-
- return name.Trim();
- }
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
deleted file mode 100644
index b71a3975f..000000000
--- a/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _config;
- private readonly IFileOrganizationService _organizationService;
- private readonly IProviderManager _providerManager;
-
- public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService, IProviderManager providerManager)
- {
- _libraryMonitor = libraryMonitor;
- _libraryManager = libraryManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _config = config;
- _organizationService = organizationService;
- _providerManager = providerManager;
- }
-
- public string Name
- {
- get { return "Organize new media files"; }
- }
-
- public string Description
- {
- get { return "Processes new files available in the configured watch folder."; }
- }
-
- public string Category
- {
- get { return "Library"; }
- }
-
- private AutoOrganizeOptions GetAutoOrganizeOptions()
- {
- return _config.GetAutoOrganizeOptions();
- }
-
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- if (GetAutoOrganizeOptions().TvOptions.IsEnabled)
- {
- await new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager)
- .Organize(GetAutoOrganizeOptions(), cancellationToken, progress).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// 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.FromMinutes(5).Ticks}
- };
- }
-
- public bool IsHidden
- {
- get { return !GetAutoOrganizeOptions().TvOptions.IsEnabled; }
- }
-
- public bool IsEnabled
- {
- get { return GetAutoOrganizeOptions().TvOptions.IsEnabled; }
- }
-
- public bool IsLogged
- {
- get { return false; }
- }
-
- public string Key
- {
- get { return "AutoOrganize"; }
- }
- }
-}
diff --git a/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
deleted file mode 100644
index 0dbd6f837..000000000
--- a/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
+++ /dev/null
@@ -1,236 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace Emby.Server.Implementations.FileOrganization
-{
- public class TvFolderOrganizer
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IFileOrganizationService _organizationService;
- private readonly IServerConfigurationManager _config;
- private readonly IProviderManager _providerManager;
-
- public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config, IProviderManager providerManager)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _libraryMonitor = libraryMonitor;
- _organizationService = organizationService;
- _config = config;
- _providerManager = providerManager;
- }
-
- private bool EnableOrganization(FileSystemMetadata fileInfo, TvFileOrganizationOptions options)
- {
- var minFileBytes = options.MinFileSizeMb * 1024 * 1024;
-
- try
- {
- return _libraryManager.IsVideoFile(fileInfo.FullName) && fileInfo.Length >= minFileBytes;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error organizing file {0}", ex, fileInfo.Name);
- }
-
- return false;
- }
-
- private bool IsValidWatchLocation(string path, List<string> libraryFolderPaths)
- {
- if (IsPathAlreadyInMediaLibrary(path, libraryFolderPaths))
- {
- _logger.Info("Folder {0} is not eligible for auto-organize because it is also part of an Emby library", path);
- return false;
- }
-
- return true;
- }
-
- private bool IsPathAlreadyInMediaLibrary(string path, List<string> libraryFolderPaths)
- {
- return libraryFolderPaths.Any(i => string.Equals(i, path, StringComparison.Ordinal) || _fileSystem.ContainsSubPath(i, path));
- }
-
- public async Task Organize(AutoOrganizeOptions options, CancellationToken cancellationToken, IProgress<double> progress)
- {
- var libraryFolderPaths = _libraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList();
-
- var watchLocations = options.TvOptions.WatchLocations
- .Where(i => IsValidWatchLocation(i, libraryFolderPaths))
- .ToList();
-
- var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
- .OrderBy(_fileSystem.GetCreationTimeUtc)
- .Where(i => EnableOrganization(i, options.TvOptions))
- .ToList();
-
- var processedFolders = new HashSet<string>();
-
- progress.Report(10);
-
- if (eligibleFiles.Count > 0)
- {
- var numComplete = 0;
-
- foreach (var file in eligibleFiles)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- try
- {
- var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.TvOptions.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
-
- if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase))
- {
- processedFolders.Add(file.DirectoryName);
- }
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error organizing episode {0}", ex, file.FullName);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= eligibleFiles.Count;
-
- progress.Report(10 + 89 * percent);
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
- progress.Report(99);
-
- foreach (var path in processedFolders)
- {
- var deleteExtensions = options.TvOptions.LeftOverFileExtensionsToDelete
- .Select(i => i.Trim().TrimStart('.'))
- .Where(i => !string.IsNullOrEmpty(i))
- .Select(i => "." + i)
- .ToList();
-
- if (deleteExtensions.Count > 0)
- {
- DeleteLeftOverFiles(path, deleteExtensions);
- }
-
- if (options.TvOptions.DeleteEmptyFolders)
- {
- if (!IsWatchFolder(path, watchLocations))
- {
- DeleteEmptyFolders(path);
- }
- }
- }
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Gets the files to organize.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{FileInfo}.</returns>
- private List<FileSystemMetadata> GetFilesToOrganize(string path)
- {
- try
- {
- return _fileSystem.GetFiles(path, true)
- .ToList();
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error getting files from {0}", ex, path);
-
- return new List<FileSystemMetadata>();
- }
- }
-
- /// <summary>
- /// Deletes the left over files.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="extensions">The extensions.</param>
- private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions)
- {
- var eligibleFiles = _fileSystem.GetFilePaths(path, extensions.ToArray(), false, true)
- .ToList();
-
- foreach (var file in eligibleFiles)
- {
- try
- {
- _fileSystem.DeleteFile(file);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting file {0}", ex, file);
- }
- }
- }
-
- /// <summary>
- /// Deletes the empty folders.
- /// </summary>
- /// <param name="path">The path.</param>
- private void DeleteEmptyFolders(string path)
- {
- try
- {
- foreach (var d in _fileSystem.GetDirectoryPaths(path))
- {
- DeleteEmptyFolders(d);
- }
-
- var entries = _fileSystem.GetFileSystemEntryPaths(path);
-
- if (!entries.Any())
- {
- try
- {
- _logger.Debug("Deleting empty directory {0}", path);
- _fileSystem.DeleteDirectory(path, false);
- }
- catch (UnauthorizedAccessException) { }
- catch (IOException) { }
- }
- }
- catch (UnauthorizedAccessException) { }
- }
-
- /// <summary>
- /// Determines if a given folder path is contained in a folder list
- /// </summary>
- /// <param name="path">The folder path to check.</param>
- /// <param name="watchLocations">A list of folders.</param>
- private bool IsWatchFolder(string path, IEnumerable<string> watchLocations)
- {
- return watchLocations.Contains(path, StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 05f78eba9..f150e4785 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -125,13 +125,6 @@ namespace Emby.Server.Implementations.HttpServer
return _appHost.CreateInstance(type);
}
- private ServiceController CreateServiceController()
- {
- var types = _restServices.Select(r => r.GetType()).ToArray();
-
- return new ServiceController(() => types);
- }
-
/// <summary>
/// Applies the request filters. Returns whether or not the request has been handled
/// and no more processing should be done.
@@ -186,7 +179,7 @@ namespace Emby.Server.Implementations.HttpServer
attributes.Sort((x, y) => x.Priority - y.Priority);
- return attributes.ToArray();
+ return attributes.ToArray(attributes.Count);
}
/// <summary>
@@ -697,11 +690,13 @@ namespace Emby.Server.Implementations.HttpServer
{
_restServices.AddRange(services);
- ServiceController = CreateServiceController();
+ ServiceController = new ServiceController();
_logger.Info("Calling ServiceStack AppHost.Init");
- ServiceController.Init(this);
+ var types = _restServices.Select(r => r.GetType()).ToArray();
+
+ ServiceController.Init(this, types);
var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
foreach (var filter in requestFilters)
@@ -741,7 +736,7 @@ namespace Emby.Server.Implementations.HttpServer
});
}
- return routes.ToArray();
+ return routes.ToArray(routes.Count);
}
public Func<string, object> GetParseFn(Type propertyType)
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 396bd8e88..7bd8fe2bf 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -6,19 +6,16 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
-using System.IO.Compression;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
-using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.Services;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using IRequest = MediaBrowser.Model.Services.IRequest;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
-using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter;
namespace Emby.Server.Implementations.HttpServer
{
@@ -193,50 +190,37 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public object ToOptimizedResult<T>(IRequest request, T dto)
{
- var compressionType = GetCompressionType(request);
- if (compressionType == null)
- {
- var contentType = request.ResponseContentType;
-
- switch (GetRealContentType(contentType))
- {
- case "application/xml":
- case "text/xml":
- case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
- return SerializeToXmlString(dto);
-
- case "application/json":
- case "text/json":
- return _jsonSerializer.SerializeToString(dto);
- }
- }
+ var contentType = request.ResponseContentType;
- // Do not use the memoryStreamFactory here, they don't place nice with compression
- using (var ms = new MemoryStream())
+ switch (GetRealContentType(contentType))
{
- var contentType = request.ResponseContentType;
- var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return SerializeToXmlString(dto);
- writerFn(dto, ms);
+ case "application/json":
+ case "text/json":
+ return _jsonSerializer.SerializeToString(dto);
+ default:
+ {
+ var ms = new MemoryStream();
+ var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
- ms.Position = 0;
+ writerFn(dto, ms);
+
+ ms.Position = 0;
- var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHttpResult(new byte[] { }, contentType, true);
+ }
- return GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result;
+ return GetHttpResult(ms, contentType, true);
+ }
}
}
- private static Stream GetCompressionStream(Stream outputStream, string compressionType)
- {
- if (compressionType == "deflate")
- return new DeflateStream(outputStream, CompressionMode.Compress, true);
- if (compressionType == "gzip")
- return new GZipStream(outputStream, CompressionMode.Compress, true);
-
- throw new NotSupportedException(compressionType);
- }
-
public static string GetRealContentType(string contentType)
{
return contentType == null
@@ -568,123 +552,47 @@ namespace Emby.Server.Implementations.HttpServer
var contentType = options.ContentType;
var responseHeaders = options.ResponseHeaders;
- var requestedCompressionType = GetCompressionType(requestContext);
+ //var requestedCompressionType = GetCompressionType(requestContext);
- if (!compress || string.IsNullOrEmpty(requestedCompressionType))
- {
- var rangeHeader = requestContext.Headers.Get("Range");
-
- if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path))
- {
- return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem)
- {
- OnComplete = options.OnComplete,
- OnError = options.OnError,
- FileShare = options.FileShare
- };
- }
-
- if (!string.IsNullOrWhiteSpace(rangeHeader))
- {
- var stream = await factoryFn().ConfigureAwait(false);
-
- return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger)
- {
- OnComplete = options.OnComplete
- };
- }
- else
- {
- var stream = await factoryFn().ConfigureAwait(false);
-
- responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
-
- if (isHeadRequest)
- {
- stream.Dispose();
-
- return GetHttpResult(new byte[] { }, contentType, true);
- }
-
- return new StreamWriter(stream, contentType, _logger)
- {
- OnComplete = options.OnComplete,
- OnError = options.OnError
- };
- }
- }
+ var rangeHeader = requestContext.Headers.Get("Range");
- using (var stream = await factoryFn().ConfigureAwait(false))
+ if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path))
{
- return await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false);
+ return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem)
+ {
+ OnComplete = options.OnComplete,
+ OnError = options.OnError,
+ FileShare = options.FileShare
+ };
}
- }
- private async Task<IHasHeaders> GetCompressedResult(Stream stream,
- string requestedCompressionType,
- IDictionary<string, string> responseHeaders,
- bool isHeadRequest,
- string contentType)
- {
- using (var reader = new MemoryStream())
+ if (!string.IsNullOrWhiteSpace(rangeHeader))
{
- await stream.CopyToAsync(reader).ConfigureAwait(false);
-
- reader.Position = 0;
- var content = reader.ToArray();
+ var stream = await factoryFn().ConfigureAwait(false);
- if (content.Length >= 1024)
+ return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger)
{
- content = Compress(content, requestedCompressionType);
- responseHeaders["Content-Encoding"] = requestedCompressionType;
- }
+ OnComplete = options.OnComplete
+ };
+ }
+ else
+ {
+ var stream = await factoryFn().ConfigureAwait(false);
- responseHeaders["Vary"] = "Accept-Encoding";
- responseHeaders["Content-Length"] = content.Length.ToString(UsCulture);
+ responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
if (isHeadRequest)
{
+ stream.Dispose();
+
return GetHttpResult(new byte[] { }, contentType, true);
}
- return GetHttpResult(content, contentType, true, responseHeaders);
- }
- }
-
- private byte[] Compress(byte[] bytes, string compressionType)
- {
- if (compressionType == "deflate")
- return Deflate(bytes);
-
- if (compressionType == "gzip")
- return GZip(bytes);
-
- throw new NotSupportedException(compressionType);
- }
-
- private byte[] Deflate(byte[] bytes)
- {
- // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
- // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
- using (var ms = new MemoryStream())
- using (var zipStream = new DeflateStream(ms, CompressionMode.Compress))
- {
- zipStream.Write(bytes, 0, bytes.Length);
- zipStream.Dispose();
-
- return ms.ToArray();
- }
- }
-
- private byte[] GZip(byte[] buffer)
- {
- using (var ms = new MemoryStream())
- using (var zipStream = new GZipStream(ms, CompressionMode.Compress))
- {
- zipStream.Write(buffer, 0, buffer.Length);
- zipStream.Dispose();
-
- return ms.ToArray();
+ return new StreamWriter(stream, contentType, _logger)
+ {
+ OnComplete = options.OnComplete,
+ OnError = options.OnError
+ };
}
}
diff --git a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
index f0e75eea4..de30dc30a 100644
--- a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
+++ b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.HttpServer
{
@@ -29,7 +30,7 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- var headerText = string.Join(", ", headers.Select(i => i.Name + "=" + i.Value).ToArray());
+ var headerText = string.Join(", ", headers.Select(i => i.Name + "=" + i.Value).ToArray(headers.Count));
logger.Info("HTTP {0} {1}. {2}", method, url, headerText);
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
index 4fbe0ed94..4e8dd7362 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Text;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
@@ -585,7 +586,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
WriteCharBytes(bytes, ch, e);
}
- byte[] buf = bytes.ToArray();
+ byte[] buf = bytes.ToArray(bytes.Count);
bytes = null;
return e.GetString(buf, 0, buf.Length);
diff --git a/Emby.Server.Core/HttpServerFactory.cs b/Emby.Server.Implementations/HttpServerFactory.cs
index e16cbea0e..b1d78e6f4 100644
--- a/Emby.Server.Core/HttpServerFactory.cs
+++ b/Emby.Server.Implementations/HttpServerFactory.cs
@@ -1,9 +1,7 @@
using System;
using System.IO;
using System.Net.Security;
-using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
-using System.Threading;
using System.Threading.Tasks;
using Emby.Common.Implementations.Net;
using Emby.Server.Implementations.HttpServer;
@@ -21,7 +19,7 @@ using MediaBrowser.Model.Text;
using ServiceStack.Text.Jsv;
using SocketHttpListener.Primitives;
-namespace Emby.Server.Core
+namespace Emby.Server.Implementations
{
/// <summary>
/// Class ServerFactory
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index d4914e734..b2554049d 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -130,14 +130,6 @@ namespace Emby.Server.Implementations.IO
paths = _affectedPaths.ToList();
}
- // Extend the timer as long as any of the paths are still being written to.
- if (paths.Any(IsFileLocked))
- {
- Logger.Info("Timer extended.");
- RestartTimer();
- return;
- }
-
Logger.Debug("Timer stopped.");
DisposeTimer();
@@ -229,90 +221,6 @@ namespace Emby.Server.Implementations.IO
return item;
}
- private bool IsFileLocked(string path)
- {
- if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
- {
- // Causing lockups on linux
- return false;
- }
-
- // Only try to open video files
- if (!_libraryManager.IsVideoFile(path))
- {
- return false;
- }
-
- try
- {
- var data = _fileSystem.GetFileSystemInfo(path);
-
- if (!data.Exists
- || data.IsDirectory
-
- // Opening a writable stream will fail with readonly files
- || data.IsReadOnly)
- {
- return false;
- }
- }
- catch (IOException)
- {
- return false;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting file system info for: {0}", ex, path);
- return false;
- }
-
- // In order to determine if the file is being written to, we have to request write access
- // But if the server only has readonly access, this is going to cause this entire algorithm to fail
- // So we'll take a best guess about our access level
- //var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
- // ? FileAccessMode.ReadWrite
- // : FileAccessMode.Read;
-
- var requestedFileAccess = FileAccessMode.Read;
- try
- {
- using (_fileSystem.GetFileStream(path, FileOpenMode.Open, requestedFileAccess, FileShareMode.ReadWrite))
- {
- //file is not locked
- return false;
- }
- }
- catch (DirectoryNotFoundException)
- {
- // File may have been deleted
- return false;
- }
- catch (FileNotFoundException)
- {
- // File may have been deleted
- return false;
- }
- catch (UnauthorizedAccessException)
- {
- Logger.Debug("No write permission for: {0}.", path);
- return false;
- }
- catch (IOException)
- {
- //the file is unavailable because it is:
- //still being written to
- //or being processed by another thread
- //or does not exist (has already been processed)
- Logger.Debug("{0} is locked.", path);
- return true;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error determining if file is locked: {0}", ex, path);
- return false;
- }
- }
-
private void DisposeTimer()
{
lock (_timerLock)
diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index ebc5e5e55..c452c01be 100644
--- a/Emby.Server.Core/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -13,9 +13,8 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
-using Emby.Server.Implementations.IO;
-namespace Emby.Server.Core.IO
+namespace Emby.Server.Implementations.IO
{
public class LibraryMonitor : ILibraryMonitor
{
diff --git a/Emby.Server.Core/IO/MemoryStreamProvider.cs b/Emby.Server.Implementations/IO/MemoryStreamProvider.cs
index f6dd1ecbc..eca76203c 100644
--- a/Emby.Server.Core/IO/MemoryStreamProvider.cs
+++ b/Emby.Server.Implementations/IO/MemoryStreamProvider.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.IO;
using Microsoft.IO;
-namespace Emby.Server.Core.IO
+namespace Emby.Server.Implementations.IO
{
public class RecyclableMemoryStreamProvider : IMemoryStreamFactory
{
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index b8ce23a42..3f9ea79c6 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -37,12 +37,12 @@ namespace Emby.Server.Implementations.Images
ImageProcessor = imageProcessor;
}
- protected virtual bool Supports(IHasImages item)
+ protected virtual bool Supports(IHasMetadata item)
{
return true;
}
- public virtual IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public virtual IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.Images
};
}
- private IEnumerable<ImageType> GetEnabledImages(IHasImages item)
+ private IEnumerable<ImageType> GetEnabledImages(IHasMetadata item)
{
//var options = ProviderManager.GetMetadataOptions(item);
@@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Images
return updateType;
}
- protected async Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ protected async Task<ItemUpdateType> FetchAsync(IHasMetadata item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var image = item.GetImageInfo(imageType, 0);
@@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.Images
return await FetchToFileInternal(item, items, imageType, cancellationToken).ConfigureAwait(false);
}
- protected async Task<ItemUpdateType> FetchToFileInternal(IHasImages item,
+ protected async Task<ItemUpdateType> FetchToFileInternal(IHasMetadata item,
List<BaseItem> itemsWithImages,
ImageType imageType,
CancellationToken cancellationToken)
@@ -132,14 +132,14 @@ namespace Emby.Server.Implementations.Images
return ItemUpdateType.ImageUpdate;
}
- protected abstract List<BaseItem> GetItemsWithImages(IHasImages item);
+ protected abstract List<BaseItem> GetItemsWithImages(IHasMetadata item);
- protected string CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreateThumbCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 640, 360);
}
- protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items)
+ protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasMetadata primaryItem, IEnumerable<BaseItem> items)
{
return items
.Select(i =>
@@ -161,22 +161,22 @@ namespace Emby.Server.Implementations.Images
.Where(i => !string.IsNullOrWhiteSpace(i));
}
- protected string CreatePosterCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreatePosterCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 400, 600);
}
- protected string CreateSquareCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreateSquareCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 600, 600);
}
- protected string CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
+ protected string CreateThumbCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
return CreateCollage(primaryItem, items, outputPath, width, height);
}
- private string CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
+ private string CreateCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath));
@@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Images
get { return "Dynamic Image Provider"; }
}
- protected virtual string CreateImage(IHasImages item,
+ protected virtual string CreateImage(IHasMetadata item,
List<BaseItem> itemsWithImages,
string outputPathWithoutExtension,
ImageType imageType,
@@ -267,7 +267,7 @@ namespace Emby.Server.Implementations.Images
return false;
}
- protected bool HasChanged(IHasImages item, ImageType type)
+ protected bool HasChanged(IHasMetadata item, ImageType type)
{
var image = item.GetImageInfo(type, 0);
@@ -293,20 +293,16 @@ namespace Emby.Server.Implementations.Images
return true;
}
- protected List<BaseItem> GetFinalItems(List<BaseItem> items)
+ protected List<BaseItem> GetFinalItems(IEnumerable<BaseItem> items)
{
return GetFinalItems(items, 4);
}
- protected virtual List<BaseItem> GetFinalItems(List<BaseItem> items, int limit)
+ protected virtual List<BaseItem> GetFinalItems(IEnumerable<BaseItem> items, int limit)
{
- // Rotate the images once every x days
- var random = DateTime.Now.DayOfYear % MaxImageAgeDays;
-
return items
- .OrderBy(i => (random + string.Empty + items.IndexOf(i)).GetMD5())
+ .OrderBy(i => Guid.NewGuid())
.Take(limit)
- .OrderBy(i => i.Name)
.ToList();
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 4846a5768..40dccf9ba 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -136,14 +136,6 @@ namespace Emby.Server.Implementations.Library
/// <value>The configuration manager.</value>
private IServerConfigurationManager ConfigurationManager { get; set; }
- /// <summary>
- /// A collection of items that may be referenced from multiple physical places in the library
- /// (typically, multiple user roots). We store them here and be sure they all reference a
- /// single instance.
- /// </summary>
- /// <value>The by reference items.</value>
- private ConcurrentDictionary<Guid, BaseItem> ByReferenceItems { get; set; }
-
private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
private readonly Func<IProviderManager> _providerManagerFactory;
private readonly Func<IUserViewManager> _userviewManager;
@@ -186,7 +178,6 @@ namespace Emby.Server.Implementations.Library
_fileSystem = fileSystem;
_providerManagerFactory = providerManagerFactory;
_userviewManager = userviewManager;
- ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -560,22 +551,6 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
- /// <summary>
- /// Ensure supplied item has only one instance throughout
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>The proper instance to the item</returns>
- public BaseItem GetOrAddByReferenceItem(BaseItem item)
- {
- // Add this item to our list if not there already
- if (!ByReferenceItems.TryAdd(item.Id, item))
- {
- // Already there - return the existing reference
- item = ByReferenceItems[item.Id];
- }
- return item;
- }
-
public BaseItem ResolvePath(FileSystemMetadata fileInfo,
Folder parent = null)
{
@@ -1197,6 +1172,8 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
+ await ItemRepository.UpdateInheritedValues(cancellationToken).ConfigureAwait(false);
+
progress.Report(100);
}
@@ -1298,7 +1275,7 @@ namespace Emby.Server.Implementations.Library
return item;
}
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
+ public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
{
if (query.Recursive && query.ParentId.HasValue)
{
@@ -1317,7 +1294,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetItemList(query);
}
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
+ public List<BaseItem> GetItemList(InternalItemsQuery query)
{
return GetItemList(query, true);
}
@@ -1341,7 +1318,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetCount(query);
}
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
+ public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
{
SetTopParentIdsOrAncestors(query, parents);
@@ -1515,9 +1492,11 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetItems(query);
}
+ var list = ItemRepository.GetItemList(query);
+
return new QueryResult<BaseItem>
{
- Items = ItemRepository.GetItemList(query).ToArray()
+ Items = list.ToArray(list.Count)
};
}
@@ -2395,8 +2374,7 @@ namespace Emby.Server.Implementations.Library
var resolver = new EpisodeResolver(GetNamingOptions(),
new NullLogger());
- var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd ||
- episode.VideoType == VideoType.HdDvd;
+ var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
var locationType = episode.LocationType;
@@ -2593,7 +2571,7 @@ namespace Emby.Server.Implementations.Library
{
var namingOptions = GetNamingOptions();
- var files = owner.DetectIsInMixedFolder() ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
+ var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.ToList();
@@ -2611,7 +2589,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
- new GenericVideoResolver<Trailer>(this)
+ new GenericVideoResolver<Trailer>(this, _fileSystem)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
@@ -2632,7 +2610,7 @@ namespace Emby.Server.Implementations.Library
return video;
// Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
+ }).OrderBy(i => i.Path);
}
private static readonly string[] ExtrasSubfolderNames = new[] { "extras", "specials", "shorts", "scenes", "featurettes", "behind the scenes", "deleted scenes", "interviews" };
@@ -2674,7 +2652,7 @@ namespace Emby.Server.Implementations.Library
return video;
// Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
+ }).OrderBy(i => i.Path);
}
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
@@ -2857,7 +2835,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.UpdatePeople(item.Id, people);
}
- public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex)
+ public async Task<ItemImageInfo> ConvertImageToLocal(IHasMetadata item, ItemImageInfo image, int imageIndex)
{
foreach (var url in image.Path.Split('|'))
{
diff --git a/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
index e64980dff..757e67eb4 100644
--- a/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.Library
Recursive = true,
DtoOptions = new DtoOptions(false)
- }).ToArray();
+ });
var numComplete = 0;
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(100);
}
- private async Task AssignTrailers(IHasTrailers item, BaseItem[] channelTrailers)
+ private async Task AssignTrailers(IHasTrailers item, IEnumerable<BaseItem> channelTrailers)
{
if (item is Game)
{
@@ -90,7 +91,7 @@ namespace Emby.Server.Implementations.Library
});
var trailerIds = trailers.Select(i => i.Id)
- .ToList();
+ .ToArray();
if (!trailerIds.SequenceEqual(item.RemoteTrailerIds))
{
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index c1360c887..6e0489d88 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -49,10 +49,9 @@ namespace Emby.Server.Implementations.Library
_providers = providers.ToArray();
}
- public IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query)
+ public List<MediaStream> GetMediaStreams(MediaStreamQuery query)
{
- var list = _itemRepo.GetMediaStreams(query)
- .ToList();
+ var list = _itemRepo.GetMediaStreams(query);
foreach (var stream in list)
{
@@ -77,7 +76,7 @@ namespace Emby.Server.Implementations.Library
return false;
}
- public IEnumerable<MediaStream> GetMediaStreams(string mediaSourceId)
+ public List<MediaStream> GetMediaStreams(string mediaSourceId)
{
var list = GetMediaStreams(new MediaStreamQuery
{
@@ -87,7 +86,7 @@ namespace Emby.Server.Implementations.Library
return GetMediaStreamsForItem(list);
}
- public IEnumerable<MediaStream> GetMediaStreams(Guid itemId)
+ public List<MediaStream> GetMediaStreams(Guid itemId)
{
var list = GetMediaStreams(new MediaStreamQuery
{
@@ -97,7 +96,7 @@ namespace Emby.Server.Implementations.Library
return GetMediaStreamsForItem(list);
}
- private IEnumerable<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams)
+ private List<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams)
{
var list = streams.ToList();
@@ -253,7 +252,7 @@ namespace Emby.Server.Implementations.Library
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
- public IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null)
+ public List<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null)
{
if (item == null)
{
@@ -265,7 +264,7 @@ namespace Emby.Server.Implementations.Library
return item.GetMediaSources(enablePathSubstitution);
}
- var sources = item.GetMediaSources(enablePathSubstitution).ToList();
+ var sources = item.GetMediaSources(enablePathSubstitution);
if (user != null)
{
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index f0d07cc3c..7f77170ad 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -19,27 +19,27 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager;
}
- public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromSong(Audio item, User user, DtoOptions dtoOptions)
{
var list = new List<Audio>
{
item
};
- return list.Concat(GetInstantMixFromGenres(item.Genres, user, dtoOptions));
+ return list.Concat(GetInstantMixFromGenres(item.Genres, user, dtoOptions)).ToList();
}
- public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromArtist(MusicArtist item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromFolder(Folder item, User user, DtoOptions dtoOptions)
{
var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user)
@@ -55,12 +55,12 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenres(genres, user, dtoOptions);
}
- public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
- public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions)
{
var genreIds = genres.DistinctNames().Select(i =>
{
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
- public IEnumerable<Audio> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -92,10 +92,10 @@ namespace Emby.Server.Implementations.Library
DtoOptions = dtoOptions
- }).Cast<Audio>();
+ });
}
- public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
{
var genre = item as MusicGenre;
if (genre != null)
@@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromFolder(folder, user, dtoOptions);
}
- return new Audio[] { };
+ return new List<BaseItem>();
}
}
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 24dc1104a..d0096de0c 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Library
var fileInfo = directoryService.GetFile(item.Path);
SetDateCreated(item, fileSystem, fileInfo);
- EnsureName(item, fileInfo);
+ EnsureName(item, item.Path, fileInfo);
}
/// <summary>
@@ -73,7 +73,7 @@ namespace Emby.Server.Implementations.Library
item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
// Make sure the item has a name
- EnsureName(item, args.FileInfo);
+ EnsureName(item, item.Path, args.FileInfo);
item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
item.GetParents().Any(i => i.IsLocked);
@@ -85,14 +85,14 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Ensures the name.
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="fileInfo">The file information.</param>
- private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo)
+ private static void EnsureName(BaseItem item, string fullPath, FileSystemMetadata fileInfo)
{
// If the subclass didn't supply a name, add it here
- if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
+ if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(fullPath))
{
- item.Name = GetDisplayName(fileInfo.Name, fileInfo.IsDirectory);
+ var fileName = fileInfo == null ? Path.GetFileName(fullPath) : fileInfo.Name;
+
+ item.Name = GetDisplayName(fileName, fileInfo != null && fileInfo.IsDirectory);
}
}
@@ -170,7 +170,11 @@ namespace Emby.Server.Implementations.Library
if (config.UseFileCreationTimeForDateAdded)
{
- item.DateCreated = fileSystem.GetCreationTimeUtc(info);
+ // directoryService.getFile may return null
+ if (info != null)
+ {
+ item.DateCreated = fileSystem.GetCreationTimeUtc(info);
+ }
}
else
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 9a5d6b105..fa4f026f4 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -6,6 +6,7 @@ using System;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
namespace Emby.Server.Implementations.Library.Resolvers
@@ -18,9 +19,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
where T : Video, new()
{
protected readonly ILibraryManager LibraryManager;
+ protected readonly IFileSystem FileSystem;
- protected BaseVideoResolver(ILibraryManager libraryManager)
+ protected BaseVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem)
{
+ FileSystem = fileSystem;
LibraryManager = libraryManager;
}
@@ -178,11 +181,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
video.VideoType = VideoType.Dvd;
}
- else if (string.Equals(videoInfo.StubType, "hddvd", StringComparison.OrdinalIgnoreCase))
- {
- video.VideoType = VideoType.HdDvd;
- video.IsHD = true;
- }
else if (string.Equals(videoInfo.StubType, "bluray", StringComparison.OrdinalIgnoreCase))
{
video.VideoType = VideoType.BluRay;
@@ -276,7 +274,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
- return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
+ return FileSystem.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 69563e5a0..1e5c0beed 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -23,11 +23,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// </summary>
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
- public MovieResolver(ILibraryManager libraryManager)
- : base(libraryManager)
- {
- }
-
/// <summary>
/// Gets the priority.
/// </summary>
@@ -74,7 +69,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- return ResolveVideos<MusicVideo>(parent, files, directoryService, false, collectionType);
+ return ResolveVideos<MusicVideo>(parent, files, directoryService, true, collectionType);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
@@ -164,8 +159,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
IsInMixedFolder = isInMixedFolder,
ProductionYear = video.Year,
Name = video.Name,
- AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToList(),
- LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToList()
+ AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToArray(),
+ LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToArray()
};
SetVideoType(videoItem, firstVideo);
@@ -452,8 +447,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
var folderPaths = multiDiscFolders.Select(i => i.FullName).Where(i =>
{
- var subFileEntries = directoryService.GetFileSystemEntries(i)
- .ToList();
+ var subFileEntries = directoryService.GetFileSystemEntries(i);
var subfolders = subFileEntries
.Where(e => e.IsDirectory)
@@ -509,7 +503,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
Path = folderPaths[0],
- AdditionalParts = folderPaths.Skip(1).ToList(),
+ AdditionalParts = folderPaths.Skip(1).ToArray(),
VideoType = videoTypes[0],
@@ -547,5 +541,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return !validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
}
+
+ public MovieResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ {
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index 8bbda3b62..04312f277 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = Path.GetFileNameWithoutExtension(args.Path);
// Make sure the image doesn't belong to a video file
- if (args.DirectoryService.GetFilePaths(_fileSystem.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename)))
+ if (_fileSystem.GetFilePaths(_fileSystem.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename)))
{
return null;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index bdab8552a..7d1c4d65a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using System.Linq;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
@@ -11,10 +12,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class EpisodeResolver : BaseVideoResolver<Episode>
{
- public EpisodeResolver(ILibraryManager libraryManager) : base(libraryManager)
- {
- }
-
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -76,5 +73,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
+
+ public EpisodeResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ {
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index b5e1bf5f7..5c7a528f5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -9,11 +10,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
public class VideoResolver : BaseVideoResolver<Video>
{
- public VideoResolver(ILibraryManager libraryManager)
- : base(libraryManager)
- {
- }
-
protected override Video Resolve(ItemResolveArgs args)
{
if (args.Parent != null)
@@ -33,12 +29,16 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
get { return ResolverPriority.Last; }
}
+
+ public VideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ {
+ }
}
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new ()
{
- public GenericVideoResolver(ILibraryManager libraryManager) : base(libraryManager)
+ public GenericVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
{
}
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 6f63322c8..658558ec0 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -10,6 +10,7 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Library
{
@@ -163,8 +164,8 @@ namespace Emby.Server.Implementations.Library
var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
NameContains = searchTerm,
- ExcludeItemTypes = excludeItemTypes.ToArray(),
- IncludeItemTypes = includeItemTypes.ToArray(),
+ ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
+ IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
Limit = query.Limit,
IncludeItemsByName = string.IsNullOrWhiteSpace(query.ParentId),
ParentId = string.IsNullOrWhiteSpace(query.ParentId) ? (Guid?)null : new Guid(query.ParentId),
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 8e8f8c4dc..019b8162a 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="user">The user.</param>
private void OnUserDeleted(User user)
{
- EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
+ EventHelper.FireEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
}
#endregion
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index a6ed84f29..0d4303b16 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Library
{
@@ -231,7 +232,7 @@ namespace Emby.Server.Implementations.Library
return list;
}
- private IEnumerable<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
+ private List<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
{
var parentId = request.ParentId;
@@ -269,7 +270,41 @@ namespace Emby.Server.Implementations.Library
return new List<BaseItem>();
}
- var excludeItemTypes = includeItemTypes.Length == 0 ? new[]
+ var mediaTypes = new List<string>();
+
+ if (includeItemTypes.Length == 0)
+ {
+ foreach (var parent in parents.OfType<ICollectionFolder>())
+ {
+ switch (parent.CollectionType)
+ {
+ case CollectionType.Books:
+ mediaTypes.Add(MediaType.Book);
+ break;
+ case CollectionType.Games:
+ mediaTypes.Add(MediaType.Game);
+ break;
+ case CollectionType.Music:
+ mediaTypes.Add(MediaType.Audio);
+ break;
+ case CollectionType.Photos:
+ mediaTypes.Add(MediaType.Photo);
+ mediaTypes.Add(MediaType.Video);
+ break;
+ case CollectionType.HomeVideos:
+ mediaTypes.Add(MediaType.Photo);
+ mediaTypes.Add(MediaType.Video);
+ break;
+ default:
+ mediaTypes.Add(MediaType.Video);
+ break;
+ }
+ }
+
+ mediaTypes = mediaTypes.Distinct().ToList();
+ }
+
+ var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[]
{
typeof(Person).Name,
typeof(Studio).Name,
@@ -290,7 +325,8 @@ namespace Emby.Server.Implementations.Library
IsVirtualItem = false,
Limit = limit * 5,
IsPlayed = isPlayed,
- DtoOptions = options
+ DtoOptions = options,
+ MediaTypes = mediaTypes.ToArray(mediaTypes.Count)
};
if (parents.Count == 0)
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index c2eb30ee4..39630cf96 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -55,28 +55,21 @@ namespace Emby.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
- var people = _libraryManager.GetPeople(new InternalPeopleQuery());
-
- var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var person in people)
- {
- dict[person.Name] = true;
- }
+ var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery());
var numComplete = 0;
- _logger.Debug("Will refresh {0} people", dict.Count);
+ var numPeople = people.Count;
- var numPeople = dict.Count;
+ _logger.Debug("Will refresh {0} people", numPeople);
- foreach (var person in dict)
+ foreach (var person in people)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
- var item = _libraryManager.GetPerson(person.Key);
+ var item = _libraryManager.GetPerson(person);
var options = new MetadataRefreshOptions(_fileSystem)
{
diff --git a/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs b/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
index 95cefd999..0c8049d8b 100644
--- a/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
@@ -28,12 +28,12 @@ namespace Emby.Server.Implementations.LiveTv
_appHost = appHost;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary };
}
- public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public async Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var liveTvItem = (LiveTvChannel)item;
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv
get { return "Live TV Service Provider"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is LiveTvChannel;
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index b55e4412b..99b5558a2 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Common.Security;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -36,7 +35,6 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Extensions;
@@ -61,7 +59,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
private readonly IProviderManager _providerManager;
- private readonly IFileOrganizationService _organizationService;
private readonly IMediaEncoder _mediaEncoder;
private readonly IProcessFactory _processFactory;
private readonly ISystemEvents _systemEvents;
@@ -74,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
- public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents)
+ public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents)
{
Current = this;
@@ -86,7 +83,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_libraryManager = libraryManager;
_libraryMonitor = libraryMonitor;
_providerManager = providerManager;
- _organizationService = organizationService;
_mediaEncoder = mediaEncoder;
_processFactory = processFactory;
_systemEvents = systemEvents;
@@ -1636,7 +1632,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- var episodesToDelete = (librarySeries.GetItems(new InternalItemsQuery
+ var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery
{
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
@@ -1646,7 +1642,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DtoOptions = new DtoOptions(true)
}))
- .Items
.Where(i => i.LocationType == LocationType.FileSystem && _fileSystem.FileExists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1)
.ToList();
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 6173068cc..48eba4117 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return "ts";
}
- return "mp4";
+ return "mkv";
}
}
@@ -207,9 +207,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
inputModifier += " -fflags " + string.Join("", flags.ToArray());
}
- if (!string.IsNullOrWhiteSpace(GetEncodingOptions().HardwareAccelerationType))
+ var videoStream = mediaSource.VideoStream;
+ var videoDecoder = videoStream == null ? null : new EncodingHelper(_mediaEncoder, _fileSystem, null).GetVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions());
+
+ if (!string.IsNullOrWhiteSpace(videoDecoder))
{
- inputModifier += " -hwaccel auto";
+ inputModifier += " " + videoDecoder;
}
if (mediaSource.ReadAtNativeFramerate)
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 5c5072192..619d2378d 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv
{
try
{
- dto.ParentBackdropImageTags = new List<string>
+ dto.ParentBackdropImageTags = new string[]
{
_imageProcessor.GetImageCacheTag(librarySeries, image)
};
@@ -218,14 +218,14 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Count == 0)
+ if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
{
image = program.GetImageInfo(ImageType.Backdrop, 0);
if (image != null)
{
try
{
- dto.ParentBackdropImageTags = new List<string>
+ dto.ParentBackdropImageTags = new string[]
{
_imageProcessor.GetImageCacheTag(program, image)
};
@@ -406,7 +406,7 @@ namespace Emby.Server.Implementations.LiveTv
return dto;
}
- internal string GetImageTag(IHasImages info)
+ internal string GetImageTag(IHasMetadata info)
{
try
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index bf11b7d3a..3fbbc8390 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -173,7 +173,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItem>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@@ -208,15 +208,7 @@ namespace Emby.Server.Implementations.LiveTv
internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
}
- var channelResult = _libraryManager.GetItemsResult(internalQuery);
-
- var result = new QueryResult<LiveTvChannel>
- {
- Items = channelResult.Items.Cast<LiveTvChannel>().ToArray(),
- TotalRecordCount = channelResult.TotalRecordCount
- };
-
- return result;
+ return _libraryManager.GetItemsResult(internalQuery);
}
public LiveTvChannel GetInternalChannel(string id)
@@ -993,7 +985,9 @@ namespace Emby.Server.Implementations.LiveTv
var queryResult = _libraryManager.QueryItems(internalQuery);
- var returnArray = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user)
+ .ConfigureAwait(false));
+ var returnArray = returnList.ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -1077,7 +1071,9 @@ namespace Emby.Server.Implementations.LiveTv
var user = _userManager.GetUserById(query.UserId);
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+ .ConfigureAwait(false));
+ var returnArray = returnList.ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -1639,7 +1635,7 @@ namespace Emby.Server.Implementations.LiveTv
{
MediaTypes = new[] { MediaType.Video },
Recursive = true,
- AncestorIds = folderIds.Select(i => i.ToString("N")).ToArray(),
+ AncestorIds = folderIds.Select(i => i.ToString("N")).ToArray(folderIds.Count),
IsFolder = false,
IsVirtualItem = false,
Limit = query.Limit,
@@ -1647,9 +1643,9 @@ namespace Emby.Server.Implementations.LiveTv
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
EnableTotalRecordCount = query.EnableTotalRecordCount,
- IncludeItemTypes = includeItemTypes.ToArray(),
- ExcludeItemTypes = excludeItemTypes.ToArray(),
- Genres = genres.ToArray(),
+ IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
+ ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
+ Genres = genres.ToArray(genres.Count),
DtoOptions = dtoOptions
});
}
@@ -1695,17 +1691,20 @@ namespace Emby.Server.Implementations.LiveTv
var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
Recursive = true,
- AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
+ AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(folders.Count),
Limit = query.Limit,
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
EnableTotalRecordCount = query.EnableTotalRecordCount,
- IncludeItemTypes = includeItemTypes.ToArray(),
- ExcludeItemTypes = excludeItemTypes.ToArray(),
+ IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
+ ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
DtoOptions = options
});
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+ .ConfigureAwait(false));
+
+ var returnArray = returnList.ToArray(returnList.Count);
return new QueryResult<BaseItemDto>
{
@@ -1845,6 +1844,9 @@ namespace Emby.Server.Implementations.LiveTv
public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, List<ItemFields> fields, User user = null)
{
var programTuples = new List<Tuple<BaseItemDto, string, string, string>>();
+ var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
+ var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo);
+ var hasServiceName = fields.Contains(ItemFields.ServiceName);
foreach (var tuple in tuples)
{
@@ -1887,7 +1889,7 @@ namespace Emby.Server.Implementations.LiveTv
dto.IsPremiere = program.IsPremiere;
}
- if (fields.Contains(ItemFields.ChannelInfo))
+ if (hasChannelInfo || hasChannelImage)
{
var channel = GetInternalChannel(program.ChannelId);
@@ -1897,7 +1899,7 @@ namespace Emby.Server.Implementations.LiveTv
dto.MediaType = channel.MediaType;
dto.ChannelNumber = channel.Number;
- if (channel.HasImage(ImageType.Primary))
+ if (hasChannelImage && channel.HasImage(ImageType.Primary))
{
dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
}
@@ -1906,7 +1908,7 @@ namespace Emby.Server.Implementations.LiveTv
var serviceName = program.ServiceName;
- if (fields.Contains(ItemFields.ServiceName))
+ if (hasServiceName)
{
dto.ServiceName = serviceName;
}
@@ -1954,7 +1956,7 @@ namespace Emby.Server.Implementations.LiveTv
if (dto.MediaSources == null)
{
- dto.MediaSources = recording.GetMediaSources(true).ToList();
+ dto.MediaSources = recording.GetMediaSources(true);
}
if (dto.MediaStreams == null)
@@ -1993,7 +1995,9 @@ namespace Emby.Server.Implementations.LiveTv
var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+ .ConfigureAwait(false));
+ var returnArray = returnList.ToArray(returnList.Count);
return new QueryResult<BaseItemDto>
{
@@ -2081,7 +2085,7 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = returnList
.OrderBy(i => i.StartDate)
- .ToArray();
+ .ToArray(returnList.Count);
return new QueryResult<TimerInfoDto>
{
@@ -2154,7 +2158,7 @@ namespace Emby.Server.Implementations.LiveTv
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
_lastRecordingRefreshTime = DateTime.MinValue;
- EventHelper.QueueEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
+ EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
{
Argument = new TimerEventInfo
{
@@ -2177,7 +2181,7 @@ namespace Emby.Server.Implementations.LiveTv
await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
_lastRecordingRefreshTime = DateTime.MinValue;
- EventHelper.QueueEventIfNotNull(SeriesTimerCancelled, this, new GenericEventArgs<TimerEventInfo>
+ EventHelper.FireEventIfNotNull(SeriesTimerCancelled, this, new GenericEventArgs<TimerEventInfo>
{
Argument = new TimerEventInfo
{
@@ -2338,7 +2342,7 @@ namespace Emby.Server.Implementations.LiveTv
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") },
DtoOptions = options
- }).ToList() : new List<BaseItem>();
+ }) : new List<BaseItem>();
RemoveFields(options);
@@ -2347,6 +2351,7 @@ namespace Emby.Server.Implementations.LiveTv
var addCurrentProgram = options.AddCurrentProgram;
var addMediaSources = options.Fields.Contains(ItemFields.MediaSources);
+ var addServiceName = options.Fields.Contains(ItemFields.ServiceName);
foreach (var tuple in tuples)
{
@@ -2356,13 +2361,17 @@ namespace Emby.Server.Implementations.LiveTv
dto.Number = channel.Number;
dto.ChannelNumber = channel.Number;
dto.ChannelType = channel.ChannelType;
- dto.ServiceName = channel.ServiceName;
+
+ if (addServiceName)
+ {
+ dto.ServiceName = channel.ServiceName;
+ }
currentChannelsDict[dto.Id] = dto;
if (addMediaSources)
{
- dto.MediaSources = channel.GetMediaSources(true).ToList();
+ dto.MediaSources = channel.GetMediaSources(true);
}
if (addCurrentProgram)
@@ -2516,7 +2525,7 @@ namespace Emby.Server.Implementations.LiveTv
_lastRecordingRefreshTime = DateTime.MinValue;
_logger.Info("New recording scheduled");
- EventHelper.QueueEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
+ EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
{
Argument = new TimerEventInfo
{
@@ -2558,7 +2567,7 @@ namespace Emby.Server.Implementations.LiveTv
_lastRecordingRefreshTime = DateTime.MinValue;
- EventHelper.QueueEventIfNotNull(SeriesTimerCreated, this, new GenericEventArgs<TimerEventInfo>
+ EventHelper.FireEventIfNotNull(SeriesTimerCreated, this, new GenericEventArgs<TimerEventInfo>
{
Argument = new TimerEventInfo
{
@@ -2697,7 +2706,7 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItemDto>
{
- Items = groups.ToArray(),
+ Items = groups.ToArray(groups.Count),
TotalRecordCount = groups.Count
};
}
@@ -2984,7 +2993,7 @@ namespace Emby.Server.Implementations.LiveTv
Name = tunerChannelId,
Value = providerChannelId
});
- listingsProviderInfo.ChannelMappings = list.ToArray();
+ listingsProviderInfo.ChannelMappings = list.ToArray(list.Count);
}
_config.SaveConfiguration("livetv", config);
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 919c0f10d..5436a12b8 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -14,6 +14,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.LiveTv
{
@@ -78,8 +79,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var hasMediaSources = (IHasMediaSources)item;
- sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
- .ToList();
+ sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false);
forceRequireOpening = true;
}
@@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.LiveTv
openKeys.Add(item.GetType().Name);
openKeys.Add(item.Id.ToString("N"));
openKeys.Add(source.Id ?? string.Empty);
- source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
+ source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray(openKeys.Count));
}
// Dummy this up so that direct play checks can still run
diff --git a/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs b/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
index 47663bdbc..992badbb5 100644
--- a/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
@@ -19,12 +19,12 @@ namespace Emby.Server.Implementations.LiveTv
_liveTvManager = liveTvManager;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary };
}
- public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public async Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var liveTvItem = (ILiveTvRecording)item;
@@ -58,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv
get { return "Live TV Service Provider"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is ILiveTvRecording;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 113cb33f4..2c12f4ca1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -11,12 +10,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
@@ -46,9 +43,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
get { return "M3U Tuner"; }
}
+ private string GetFullChannelIdPrefix(TunerHostInfo info)
+ {
+ return ChannelIdPrefix + info.Url.GetMD5().ToString("N");
+ }
+
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
- var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
+ var channelIdPrefix = GetFullChannelIdPrefix(info);
+
+ var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
return result.Cast<ChannelInfo>().ToList();
}
@@ -87,9 +91,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
{
- var urlHash = info.Url.GetMD5().ToString("N");
- var prefix = ChannelIdPrefix + urlHash;
- if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ var channelIdPrefix = GetFullChannelIdPrefix(info);
+
+ if (!channelId.StartsWith(channelIdPrefix, StringComparison.OrdinalIgnoreCase))
{
return null;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 8d73c7e2b..113e691b6 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -32,25 +32,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
_appHost = appHost;
}
- public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier, CancellationToken cancellationToken)
+ public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
{
- var urlHash = url.GetMD5().ToString("N");
-
// Read the file and display it line by line.
using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
{
- return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, enableStreamUrlAsIdentifier);
+ return GetChannels(reader, channelIdPrefix, tunerHostId);
}
}
public List<M3UChannel> ParseString(string text, string channelIdPrefix, string tunerHostId)
{
- var urlHash = "text".GetMD5().ToString("N");
-
// Read the file and display it line by line.
using (var reader = new StringReader(text))
{
- return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, false);
+ return GetChannels(reader, channelIdPrefix, tunerHostId);
}
}
@@ -70,7 +66,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
const string ExtInfPrefix = "#EXTINF:";
- private List<M3UChannel> GetChannels(TextReader reader, string urlHash, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier)
+ private List<M3UChannel> GetChannels(TextReader reader, string channelIdPrefix, string tunerHostId)
{
var channels = new List<M3UChannel>();
string line;
@@ -97,13 +93,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
{
var channel = GetChannelnfo(extInf, tunerHostId, line);
- if (string.IsNullOrWhiteSpace(channel.Id) || enableStreamUrlAsIdentifier)
+ if (string.IsNullOrWhiteSpace(channel.Id))
{
- channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
+ channel.Id = channelIdPrefix + line.GetMD5().ToString("N");
}
else
{
- channel.Id = channelIdPrefix + urlHash + channel.Id.GetMD5().ToString("N");
+ channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N");
}
channel.Path = line;
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index bc0dc236d..133644ff7 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -69,7 +69,7 @@
"ViewTypeTvResume": "Resume",
"ViewTypeTvNextUp": "Next Up",
"ViewTypeTvLatest": "Latest",
- "ViewTypeTvShowSeries": "Series",
+ "ViewTypeTvShowSeries": "Shows",
"ViewTypeTvGenres": "Genres",
"ViewTypeTvFavoriteSeries": "Favorite Series",
"ViewTypeTvFavoriteEpisodes": "Favorite Episodes",
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 64a41acef..8d3051a89 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Localization
/// Gets the cultures.
/// </summary>
/// <returns>IEnumerable{CultureDto}.</returns>
- public IEnumerable<CultureDto> GetCultures()
+ public List<CultureDto> GetCultures()
{
var type = GetType();
var path = type.Namespace + ".iso6392.txt";
@@ -169,14 +169,14 @@ namespace Emby.Server.Implementations.Localization
return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
!string.IsNullOrWhiteSpace(i.DisplayName) &&
!string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
- !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName));
+ !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToList();
}
/// <summary>
/// Gets the countries.
/// </summary>
/// <returns>IEnumerable{CountryInfo}.</returns>
- public IEnumerable<CountryInfo> GetCountries()
+ public List<CountryInfo> GetCountries()
{
var type = GetType();
var path = type.Namespace + ".countries.json";
diff --git a/Emby.Server.Implementations/Localization/Ratings/es.txt b/Emby.Server.Implementations/Localization/Ratings/es.txt
index 1ba24fb99..32a1e6438 100644
--- a/Emby.Server.Implementations/Localization/Ratings/es.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/es.txt
@@ -1,4 +1,5 @@
ES-A,1
+ES-APTA,1
ES-7,3
ES-12,6
ES-16,8
diff --git a/Emby.Server.Implementations/Localization/Ratings/ro.txt b/Emby.Server.Implementations/Localization/Ratings/ro.txt
new file mode 100644
index 000000000..3fdaed9cc
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Ratings/ro.txt
@@ -0,0 +1 @@
+RO-AG,1 \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Ratings/us.txt b/Emby.Server.Implementations/Localization/Ratings/us.txt
index 3f5311e0e..9bd78c72b 100644
--- a/Emby.Server.Implementations/Localization/Ratings/us.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/us.txt
@@ -1,3 +1,4 @@
+APPROVED,1
G,1
E,1
EC,1
diff --git a/Emby.Server.Core/Localization/TextLocalizer.cs b/Emby.Server.Implementations/Localization/TextLocalizer.cs
index 1e8ccbbfa..5188a959e 100644
--- a/Emby.Server.Core/Localization/TextLocalizer.cs
+++ b/Emby.Server.Implementations/Localization/TextLocalizer.cs
@@ -3,9 +3,8 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using Emby.Server.Implementations.Localization;
-namespace Emby.Server.Core.Localization
+namespace Emby.Server.Implementations.Localization
{
public class TextLocalizer : ITextLocalizer
{
diff --git a/Emby.Server.Core/Logging/ConsoleLogger.cs b/Emby.Server.Implementations/Logging/ConsoleLogger.cs
index 01eca7b7e..51199f08a 100644
--- a/Emby.Server.Core/Logging/ConsoleLogger.cs
+++ b/Emby.Server.Implementations/Logging/ConsoleLogger.cs
@@ -1,10 +1,7 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
-namespace Emby.Server.Core.Logging
+namespace Emby.Server.Implementations.Logging
{
public class ConsoleLogger : IConsoleLogger
{
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 1d74e8788..7ee6e9e38 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -29,9 +29,9 @@ namespace Emby.Server.Implementations.MediaEncoder
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
- public EncodingManager(IFileSystem fileSystem,
- ILogger logger,
- IMediaEncoder encoder,
+ public EncodingManager(IFileSystem fileSystem,
+ ILogger logger,
+ IMediaEncoder encoder,
IChapterManager chapterManager, ILibraryManager libraryManager)
{
_fileSystem = fileSystem;
@@ -45,7 +45,7 @@ namespace Emby.Server.Implementations.MediaEncoder
/// Gets the chapter images data path.
/// </summary>
/// <value>The chapter images data path.</value>
- private string GetChapterImagesPath(IHasImages item)
+ private string GetChapterImagesPath(IHasMetadata item)
{
return Path.Combine(item.GetInternalMetadataPath(), "chapters");
}
@@ -84,13 +84,8 @@ namespace Emby.Server.Implementations.MediaEncoder
/// </summary>
private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
- public async Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken)
+ public async Task<bool> RefreshChapterImages(Video video, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
{
- var extractImages = options.ExtractImages;
- var video = options.Video;
- var chapters = options.Chapters;
- var saveChapters = options.SaveChapters;
-
if (!IsEligibleForChapterImageExtraction(video))
{
extractImages = false;
@@ -117,16 +112,14 @@ namespace Emby.Server.Implementations.MediaEncoder
{
if (extractImages)
{
- if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso)
+ if (video.VideoType == VideoType.Iso)
{
continue;
}
+
if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
{
- if (video.PlayableStreamFileNames.Count != 1)
- {
- continue;
- }
+ continue;
}
try
@@ -136,13 +129,13 @@ namespace Emby.Server.Implementations.MediaEncoder
var protocol = MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);
+ var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, new List<string>());
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
var container = video.Container;
- var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
+ var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
_fileSystem.CopyFile(tempFile, path, true);
try
@@ -151,7 +144,7 @@ namespace Emby.Server.Implementations.MediaEncoder
}
catch
{
-
+
}
chapter.ImagePath = path;
@@ -181,7 +174,7 @@ namespace Emby.Server.Implementations.MediaEncoder
if (saveChapters && changesMade)
{
- await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
+ await _chapterManager.SaveChapters(video.Id.ToString(), chapters).ConfigureAwait(false);
}
DeleteDeadImages(currentImages, chapters);
diff --git a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
index 40752db80..ff152c9e9 100644
--- a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
+++ b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Notifications;
using SQLitePCL.pretty;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Notifications
{
@@ -83,13 +84,13 @@ namespace Emby.Server.Implementations.Notifications
clauses.Add("UserId=?");
paramList.Add(query.UserId.ToGuidBlob());
- var whereClause = " where " + string.Join(" And ", clauses.ToArray());
+ var whereClause = " where " + string.Join(" And ", clauses.ToArray(clauses.Count));
using (WriteLock.Read())
{
using (var connection = CreateConnection(true))
{
- result.TotalRecordCount = connection.Query("select count(Id) from Notifications" + whereClause, paramList.ToArray()).SelectScalarInt().First();
+ result.TotalRecordCount = connection.Query("select count(Id) from Notifications" + whereClause, paramList.ToArray(paramList.Count)).SelectScalarInt().First();
var commandText = string.Format("select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause);
@@ -110,12 +111,12 @@ namespace Emby.Server.Implementations.Notifications
var resultList = new List<Notification>();
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ foreach (var row in connection.Query(commandText, paramList.ToArray(paramList.Count)))
{
resultList.Add(GetNotification(row));
}
- result.Notifications = resultList.ToArray();
+ result.Notifications = resultList.ToArray(resultList.Count);
}
}
@@ -280,7 +281,7 @@ namespace Emby.Server.Implementations.Notifications
public async Task MarkRead(IEnumerable<string> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken)
{
var list = notificationIdList.ToList();
- var idArray = list.Select(i => new Guid(i)).ToArray();
+ var idArray = list.Select(i => new Guid(i)).ToArray(list.Count);
await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false);
@@ -290,7 +291,7 @@ namespace Emby.Server.Implementations.Notifications
{
NotificationsMarkedRead(this, new NotificationReadEventArgs
{
- IdList = list.ToArray(),
+ IdList = list.ToArray(list.Count),
IsRead = isRead,
UserId = userId
});
diff --git a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
index 0239ee336..0d89ba84f 100644
--- a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
+++ b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
using System.Linq;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Notifications
{
@@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Notifications
list.Add(e.UserId);
list.Add(e.IsRead.ToString().ToLower());
- var msg = string.Join("|", list.ToArray());
+ var msg = string.Join("|", list.ToArray(list.Count));
_serverManager.SendWebSocketMessage("NotificationsMarkedRead", msg);
}
diff --git a/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
index 17f9b491d..f7c65f63d 100644
--- a/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
+++ b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
@@ -18,15 +18,15 @@ namespace Emby.Server.Implementations.Photos
{
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var photoAlbum = (PhotoAlbum)item;
- var items = GetFinalItems(photoAlbum.Children.ToList());
+ var items = GetFinalItems(photoAlbum.Children);
return items;
}
- protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 127ce24ae..c2e860339 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Playlists
{
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var playlist = (Playlist)item;
@@ -65,8 +65,7 @@ namespace Emby.Server.Implementations.Playlists
return null;
})
.Where(i => i != null)
- .DistinctBy(i => i.Id)
- .ToList();
+ .DistinctBy(i => i.Id);
return GetFinalItems(items);
}
@@ -81,7 +80,7 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -93,12 +92,12 @@ namespace Emby.Server.Implementations.Playlists
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
- }).ToList();
+ });
return GetFinalItems(items);
}
- //protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ //protected override Task<string> CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
//{
// return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
//}
@@ -113,7 +112,7 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -125,12 +124,12 @@ namespace Emby.Server.Implementations.Playlists
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
- }).ToList();
+ });
return GetFinalItems(items);
}
- //protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ //protected override Task<string> CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
//{
// return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
//}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index e0e133e38..578a2321c 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -12,10 +12,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-
using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Playlists
{
@@ -164,7 +163,7 @@ namespace Emby.Server.Implementations.Playlists
return path;
}
- private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user, DtoOptions options)
+ private List<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user, DtoOptions options)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
@@ -206,7 +205,9 @@ namespace Emby.Server.Implementations.Playlists
list.Add(LinkedChild.Create(item));
}
- playlist.LinkedChildren.AddRange(list);
+ var newList = playlist.LinkedChildren.ToList();
+ newList.AddRange(list);
+ playlist.LinkedChildren = newList.ToArray(newList.Count);
await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
@@ -234,7 +235,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.LinkedChildren = children.Except(removals)
.Select(i => i.Item1)
- .ToList();
+ .ToArray();
await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
@@ -265,17 +266,21 @@ namespace Emby.Server.Implementations.Playlists
var item = playlist.LinkedChildren[oldIndex];
- playlist.LinkedChildren.Remove(item);
+ var newList = playlist.LinkedChildren.ToList();
- if (newIndex >= playlist.LinkedChildren.Count)
+ newList.Remove(item);
+
+ if (newIndex >= newList.Count)
{
- playlist.LinkedChildren.Add(item);
+ newList.Add(item);
}
else
{
- playlist.LinkedChildren.Insert(newIndex, item);
+ newList.Insert(newIndex, item);
}
+ playlist.LinkedChildren = newList.ToArray(newList.Count);
+
await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
index 967e7ddd8..ec371c741 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
@@ -15,6 +15,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.ScheduledTasks
{
@@ -123,16 +124,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
try
{
- var chapters = _itemRepo.GetChapters(video.Id).ToList();
+ var chapters = _itemRepo.GetChapters(video.Id);
- var success = await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions
- {
- SaveChapters = true,
- ExtractImages = extract,
- Video = video,
- Chapters = chapters
-
- }, CancellationToken.None);
+ var success = await _encodingManager.RefreshChapterImages(video, chapters, extract, true, CancellationToken.None);
if (!success)
{
@@ -142,7 +136,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
_fileSystem.CreateDirectory(parentPath);
- _fileSystem.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray()));
+ _fileSystem.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray(previouslyFailedImages.Count)));
}
numComplete++;
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 9ec0af6bb..d512ff4fb 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using SQLitePCL.pretty;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Security
{
@@ -174,13 +175,13 @@ namespace Emby.Server.Implementations.Security
var whereTextWithoutPaging = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
if (startIndex > 0)
{
var pagingWhereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})",
pagingWhereText,
@@ -189,7 +190,7 @@ namespace Emby.Server.Implementations.Security
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
commandText += whereText;
@@ -236,7 +237,7 @@ namespace Emby.Server.Implementations.Security
}
}
- result.Items = list.ToArray();
+ result.Items = list.ToArray(list.Count);
return result;
}, ReadTransactionMode);
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index 1c530144c..4ad56411a 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -15,17 +15,15 @@ namespace Emby.Server.Implementations.Services
public class ServiceController
{
public static ServiceController Instance;
- private readonly Func<IEnumerable<Type>> _resolveServicesFn;
- public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
+ public ServiceController()
{
Instance = this;
- _resolveServicesFn = resolveServicesFn;
}
- public void Init(HttpListenerHost appHost)
+ public void Init(HttpListenerHost appHost, Type[] serviceTypes)
{
- foreach (var serviceType in _resolveServicesFn())
+ foreach (var serviceType in serviceTypes)
{
RegisterService(appHost, serviceType);
}
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index e0b5e69c0..4a2199c89 100644
--- a/Emby.Server.Implementations/Services/ServiceExec.cs
+++ b/Emby.Server.Implementations/Services/ServiceExec.cs
@@ -5,6 +5,7 @@ using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.Services
}
if (reqFilters.Count > 0)
- actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray();
+ actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray(reqFilters.Count);
actions.Add(actionCtx);
}
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index 255b20919..da5e74f1f 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -142,13 +143,13 @@ namespace Emby.Server.Implementations.Services
}
}
- var components = componentsList.ToArray();
+ var components = componentsList.ToArray(componentsList.Count);
this.TotalComponentsCount = components.Length;
this.literalsToMatch = new string[this.TotalComponentsCount];
this.variablesNames = new string[this.TotalComponentsCount];
this.isWildcard = new bool[this.TotalComponentsCount];
- this.componentsWithSeparators = hasSeparators.ToArray();
+ this.componentsWithSeparators = hasSeparators.ToArray(hasSeparators.Count);
this.PathComponentsCount = this.componentsWithSeparators.Length;
string firstLiteralMatch = null;
@@ -268,7 +269,7 @@ namespace Emby.Server.Implementations.Services
propertyInfos.InsertRange(0, newPropertyInfos);
}
- return propertyInfos.ToArray();
+ return propertyInfos.ToArray(propertyInfos.Count);
}
return GetTypesPublicProperties(type)
@@ -285,7 +286,7 @@ namespace Emby.Server.Implementations.Services
if (mi != null && mi.IsStatic) continue;
pis.Add(pi);
}
- return pis.ToArray();
+ return pis.ToArray(pis.Count);
}
@@ -450,7 +451,7 @@ namespace Emby.Server.Implementations.Services
}
}
- withPathInfoParts = totalComponents.ToArray();
+ withPathInfoParts = totalComponents.ToArray(totalComponents.Count);
return true;
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 68ab25947..9a1a229c5 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -32,6 +32,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Session
{
@@ -1000,7 +1001,7 @@ namespace Emby.Server.Implementations.Session
command.PlayCommand = PlayCommand.PlayNow;
}
- command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray();
+ command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray(items.Count);
if (user != null)
{
@@ -1033,7 +1034,7 @@ namespace Emby.Server.Implementations.Session
if (episodes.Count > 0)
{
- command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray();
+ command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray(episodes.Count);
}
}
}
@@ -1089,7 +1090,7 @@ namespace Emby.Server.Implementations.Session
{
var folder = (Folder)item;
- var itemsResult = folder.GetItems(new InternalItemsQuery(user)
+ var itemsResult = folder.GetItemList(new InternalItemsQuery(user)
{
Recursive = true,
IsFolder = false,
@@ -1104,7 +1105,7 @@ namespace Emby.Server.Implementations.Session
});
- return FilterToSingleMediaType(itemsResult.Items)
+ return FilterToSingleMediaType(itemsResult)
.OrderBy(i => i.SortName)
.ToList();
}
@@ -1675,7 +1676,6 @@ namespace Emby.Server.Implementations.Session
dtoOptions.Fields.Remove(ItemFields.ExternalEtag);
dtoOptions.Fields.Remove(ItemFields.InheritedParentalRatingValue);
dtoOptions.Fields.Remove(ItemFields.ItemCounts);
- dtoOptions.Fields.Remove(ItemFields.Keywords);
dtoOptions.Fields.Remove(ItemFields.MediaSourceCount);
dtoOptions.Fields.Remove(ItemFields.MediaStreams);
dtoOptions.Fields.Remove(ItemFields.MediaSources);
@@ -1686,7 +1686,6 @@ namespace Emby.Server.Implementations.Session
dtoOptions.Fields.Remove(ItemFields.RecursiveItemCount);
dtoOptions.Fields.Remove(ItemFields.RemoteTrailers);
dtoOptions.Fields.Remove(ItemFields.SeasonUserData);
- dtoOptions.Fields.Remove(ItemFields.SeriesGenres);
dtoOptions.Fields.Remove(ItemFields.Settings);
dtoOptions.Fields.Remove(ItemFields.SortName);
dtoOptions.Fields.Remove(ItemFields.Tags);
diff --git a/Emby.Server.Implementations/Social/SharingRepository.cs b/Emby.Server.Implementations/Social/SharingRepository.cs
index 46e9205bb..a2a1f879a 100644
--- a/Emby.Server.Implementations/Social/SharingRepository.cs
+++ b/Emby.Server.Implementations/Social/SharingRepository.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Social;
using SQLitePCL.pretty;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Social
{
@@ -86,7 +87,7 @@ namespace Emby.Server.Implementations.Social
var paramList = new List<object>();
paramList.Add(id.ToGuidBlob());
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ foreach (var row in connection.Query(commandText, paramList.ToArray(paramList.Count)))
{
return GetSocialShareInfo(row);
}
diff --git a/Emby.Server.Implementations/Sorting/AirTimeComparer.cs b/Emby.Server.Implementations/Sorting/AirTimeComparer.cs
deleted file mode 100644
index bc05e9af3..000000000
--- a/Emby.Server.Implementations/Sorting/AirTimeComparer.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace Emby.Server.Implementations.Sorting
-{
- public class AirTimeComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return DateTime.Compare(GetValue(x), GetValue(y));
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private DateTime GetValue(BaseItem x)
- {
- var series = x as Series;
-
- if (series == null)
- {
- var season = x as Season;
-
- if (season != null)
- {
- series = season.Series;
- }
- else
- {
- var episode = x as Episode;
-
- if (episode != null)
- {
- series = episode.Series;
- }
- }
- }
-
- if (series != null)
- {
- DateTime result;
- if (DateTime.TryParse(series.AirTime, out result))
- {
- return result;
- }
- }
-
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.AirTime; }
- }
- }
-}
diff --git a/Emby.Server.Core/SystemEvents.cs b/Emby.Server.Implementations/SystemEvents.cs
index 8d5cd4ad8..dfff92f1e 100644
--- a/Emby.Server.Core/SystemEvents.cs
+++ b/Emby.Server.Implementations/SystemEvents.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Common.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
-namespace MediaBrowser.Server.Startup.Common
+namespace Emby.Server.Implementations
{
public class SystemEvents : ISystemEvents
{
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 876c5d58b..03283031e 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.TV
int? limit = null;
if (!string.IsNullOrWhiteSpace(request.SeriesId))
{
- var series = _libraryManager.GetItemById(request.SeriesId);
+ var series = _libraryManager.GetItemById(request.SeriesId) as Series;
if (series != null)
{
@@ -51,17 +51,22 @@ namespace Emby.Server.Implementations.TV
}
}
- if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue)
+ if (!string.IsNullOrWhiteSpace(presentationUniqueKey))
+ {
+ return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
+ }
+
+ if (limit.HasValue)
{
limit = limit.Value + 10;
}
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(Series).Name },
- SortBy = new[] { ItemSortBy.SeriesDatePlayed },
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ SortBy = new[] { ItemSortBy.DatePlayed },
SortOrder = SortOrder.Descending,
- PresentationUniqueKey = presentationUniqueKey,
+ SeriesPresentationUniqueKey = presentationUniqueKey,
Limit = limit,
ParentId = parentIdGuid,
Recursive = true,
@@ -69,11 +74,12 @@ namespace Emby.Server.Implementations.TV
{
Fields = new List<ItemFields>
{
- ItemFields.PresentationUniqueKey
+ ItemFields.SeriesPresentationUniqueKey
}
- }
+ },
+ GroupBySeriesPresentationUniqueKey = true
- }).Cast<Series>().Select(GetUniqueSeriesKey);
+ }).Cast<Episode>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
@@ -94,7 +100,7 @@ namespace Emby.Server.Implementations.TV
int? limit = null;
if (!string.IsNullOrWhiteSpace(request.SeriesId))
{
- var series = _libraryManager.GetItemById(request.SeriesId);
+ var series = _libraryManager.GetItemById(request.SeriesId) as Series;
if (series != null)
{
@@ -103,28 +109,34 @@ namespace Emby.Server.Implementations.TV
}
}
- if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue)
+ if (!string.IsNullOrWhiteSpace(presentationUniqueKey))
+ {
+ return GetResult(GetNextUpEpisodes(request, user, new [] { presentationUniqueKey }, dtoOptions), request);
+ }
+
+ if (limit.HasValue)
{
limit = limit.Value + 10;
}
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(Series).Name },
- SortBy = new[] { ItemSortBy.SeriesDatePlayed },
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ SortBy = new[] { ItemSortBy.DatePlayed },
SortOrder = SortOrder.Descending,
- PresentationUniqueKey = presentationUniqueKey,
+ SeriesPresentationUniqueKey = presentationUniqueKey,
Limit = limit,
DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
{
Fields = new List<ItemFields>
{
- ItemFields.PresentationUniqueKey
+ ItemFields.SeriesPresentationUniqueKey
},
EnableImages = false
- }
+ },
+ GroupBySeriesPresentationUniqueKey = true
- }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Series>().Select(GetUniqueSeriesKey);
+ }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
@@ -167,7 +179,12 @@ namespace Emby.Server.Implementations.TV
.Where(i => i != null);
}
- private string GetUniqueSeriesKey(BaseItem series)
+ private string GetUniqueSeriesKey(Episode episode)
+ {
+ return episode.SeriesPresentationUniqueKey;
+ }
+
+ private string GetUniqueSeriesKey(Series series)
{
return series.GetPresentationUniqueKey();
}
diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
index f54613384..98e0c4080 100644
--- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.UserViews
{
}
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public override IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -36,13 +36,13 @@ namespace Emby.Server.Implementations.UserViews
};
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var view = (CollectionFolder)item;
var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- var result = view.GetItems(new InternalItemsQuery
+ var result = view.GetItemList(new InternalItemsQuery
{
CollapseBoxSetItems = false,
Recursive = recursive,
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.UserViews
});
- var items = result.Items.Select(i =>
+ var items = result.Select(i =>
{
var episode = i as Episode;
if (episode != null)
@@ -91,15 +91,15 @@ namespace Emby.Server.Implementations.UserViews
}).DistinctBy(i => i.Id);
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
+ return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
}
- protected override bool Supports(IHasImages item)
+ protected override bool Supports(IHasMetadata item)
{
return item is CollectionFolder;
}
- protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.UserViews
_libraryManager = libraryManager;
}
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public override IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -134,7 +134,7 @@ namespace Emby.Server.Implementations.UserViews
};
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var view = (ManualCollectionsFolder)item;
@@ -149,15 +149,15 @@ namespace Emby.Server.Implementations.UserViews
DtoOptions = new DtoOptions(false)
});
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
+ return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
}
- protected override bool Supports(IHasImages item)
+ protected override bool Supports(IHasMetadata item)
{
return item is ManualCollectionsFolder;
}
- protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
index cd2c4728f..885dfec58 100644
--- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.UserViews
_libraryManager = libraryManager;
}
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public override IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
var view = (UserView)item;
if (IsUsingCollectionStrip(view))
@@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.UserViews
};
}
- protected override List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
{
var view = (UserView)item;
@@ -62,27 +62,27 @@ namespace Emby.Server.Implementations.UserViews
IsMovie = true,
DtoOptions = new DtoOptions(false)
- }).ToList();
+ });
- return GetFinalItems(programs).ToList();
+ return GetFinalItems(programs);
}
if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) ||
string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase))
{
- var userItemsResult = view.GetItems(new InternalItemsQuery
+ var userItemsResult = view.GetItemList(new InternalItemsQuery
{
CollapseBoxSetItems = false,
DtoOptions = new DtoOptions(false)
});
- return userItemsResult.Items.ToList();
+ return userItemsResult.ToList();
}
var isUsingCollectionStrip = IsUsingCollectionStrip(view);
var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- var result = view.GetItems(new InternalItemsQuery
+ var result = view.GetItemList(new InternalItemsQuery
{
User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null,
CollapseBoxSetItems = false,
@@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.UserViews
DtoOptions = new DtoOptions(false)
});
- var items = result.Items.Select(i =>
+ var items = result.Select(i =>
{
var episode = i as Episode;
if (episode != null)
@@ -133,13 +133,13 @@ namespace Emby.Server.Implementations.UserViews
if (isUsingCollectionStrip)
{
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
+ return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
}
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary)).ToList());
+ return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary)));
}
- protected override bool Supports(IHasImages item)
+ protected override bool Supports(IHasMetadata item)
{
var view = item as UserView;
if (view != null)
@@ -163,7 +163,7 @@ namespace Emby.Server.Implementations.UserViews
return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
}
- protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
if (itemsWithImages.Count == 0)
{
diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
index 03336c936..3675e1950 100644
--- a/Emby.Server.Implementations/packages.config
+++ b/Emby.Server.Implementations/packages.config
@@ -2,6 +2,9 @@
<packages>
<package id="Emby.XmlTv" version="1.0.9" targetFramework="net46" />
<package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
+ <package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.2" targetFramework="net46" />
+ <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
+ <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
- <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 34d0bd413..21552617d 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -1,27 +1,15 @@
-using MediaBrowser.Api.Playback;
-using MediaBrowser.Common.Configuration;
+using System;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Threading;
+using System.Collections.Generic;
+using System.Threading;
namespace MediaBrowser.Api
{
@@ -53,11 +41,6 @@ namespace MediaBrowser.Api
public readonly ITimerFactory TimerFactory;
public readonly IProcessFactory ProcessFactory;
- /// <summary>
- /// The active transcoding jobs
- /// </summary>
- private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
-
private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks =
new Dictionary<string, SemaphoreSlim>();
@@ -81,39 +64,21 @@ namespace MediaBrowser.Api
ResultFactory = resultFactory;
Instance = this;
- _sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
- _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
}
- public SemaphoreSlim GetTranscodingLock(string outputPath)
+ public static string[] Split(string value, char separator, bool removeEmpty)
{
- lock (_transcodingLocks)
+ if (string.IsNullOrWhiteSpace(value))
{
- SemaphoreSlim result;
- if (!_transcodingLocks.TryGetValue(outputPath, out result))
- {
- result = new SemaphoreSlim(1, 1);
- _transcodingLocks[outputPath] = result;
- }
-
- return result;
+ return new string[] { };
}
- }
- private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
+ if (removeEmpty)
{
- PingTranscodingJob(e.PlaySessionId, e.IsPaused);
+ return value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
}
- }
- void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
- {
- if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
- {
- PingTranscodingJob(e.PlaySessionId, e.IsPaused);
- }
+ return value.Split(separator);
}
/// <summary>
@@ -121,41 +86,6 @@ namespace MediaBrowser.Api
/// </summary>
public void Run()
{
- try
- {
- DeleteEncodedMediaCache();
- }
- catch (FileNotFoundException)
- {
- // Don't clutter the log
- }
- catch (IOException)
- {
- // Don't clutter the log
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error deleting encoded media cache", ex);
- }
- }
-
- public EncodingOptions GetEncodingOptions()
- {
- return _config.GetConfiguration<EncodingOptions>("encoding");
- }
-
- /// <summary>
- /// Deletes the encoded media cache.
- /// </summary>
- private void DeleteEncodedMediaCache()
- {
- var path = _config.ApplicationPaths.TranscodingTempPath;
-
- foreach (var file in _fileSystem.GetFilePaths(path, true)
- .ToList())
- {
- _fileSystem.DeleteFile(file);
- }
}
/// <summary>
@@ -163,660 +93,6 @@ namespace MediaBrowser.Api
/// </summary>
public void Dispose()
{
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- var list = _activeTranscodingJobs.ToList();
- var jobCount = list.Count;
-
- Parallel.ForEach(list, j => KillTranscodingJob(j, false, path => true));
-
- // Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
- if (jobCount > 0)
- {
- var task = Task.Delay(1000);
- Task.WaitAll(task);
- }
- }
-
- /// <summary>
- /// Called when [transcode beginning].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="playSessionId">The play session identifier.</param>
- /// <param name="liveStreamId">The live stream identifier.</param>
- /// <param name="transcodingJobId">The transcoding job identifier.</param>
- /// <param name="type">The type.</param>
- /// <param name="process">The process.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="state">The state.</param>
- /// <param name="cancellationTokenSource">The cancellation token source.</param>
- /// <returns>TranscodingJob.</returns>
- public TranscodingJob OnTranscodeBeginning(string path,
- string playSessionId,
- string liveStreamId,
- string transcodingJobId,
- TranscodingJobType type,
- IProcess process,
- string deviceId,
- StreamState state,
- CancellationTokenSource cancellationTokenSource)
- {
- lock (_activeTranscodingJobs)
- {
- var job = new TranscodingJob(Logger, TimerFactory)
- {
- Type = type,
- Path = path,
- Process = process,
- ActiveRequestCount = 1,
- DeviceId = deviceId,
- CancellationTokenSource = cancellationTokenSource,
- Id = transcodingJobId,
- PlaySessionId = playSessionId,
- LiveStreamId = liveStreamId,
- MediaSource = state.MediaSource
- };
-
- _activeTranscodingJobs.Add(job);
-
- ReportTranscodingProgress(job, state, null, null, null, null, null);
-
- return job;
- }
- }
-
- public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
- {
- var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
-
- if (job != null)
- {
- job.Framerate = framerate;
- job.CompletionPercentage = percentComplete;
- job.TranscodingPositionTicks = ticks;
- job.BytesTranscoded = bytesTranscoded;
- job.BitRate = bitRate;
- }
-
- var deviceId = state.Request.DeviceId;
-
- if (!string.IsNullOrWhiteSpace(deviceId))
- {
- var audioCodec = state.ActualOutputAudioCodec;
- var videoCodec = state.ActualOutputVideoCodec;
-
- _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
- {
- Bitrate = bitRate ?? state.TotalOutputBitrate,
- AudioCodec = audioCodec,
- VideoCodec = videoCodec,
- Container = state.OutputContainer,
- Framerate = framerate,
- CompletionPercentage = percentComplete,
- Width = state.OutputWidth,
- Height = state.OutputHeight,
- AudioChannels = state.OutputAudioChannels,
- IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
- IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase),
- TranscodeReasons = state.TranscodeReasons
- });
- }
- }
-
- /// <summary>
- /// <summary>
- /// The progressive
- /// </summary>
- /// Called when [transcode failed to start].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="type">The type.</param>
- /// <param name="state">The state.</param>
- public void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
- {
- lock (_activeTranscodingJobs)
- {
- var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
-
- if (job != null)
- {
- _activeTranscodingJobs.Remove(job);
- }
- }
-
- lock (_transcodingLocks)
- {
- _transcodingLocks.Remove(path);
- }
-
- if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
- {
- _sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
- }
- }
-
- /// <summary>
- /// Determines whether [has active transcoding job] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="type">The type.</param>
- /// <returns><c>true</c> if [has active transcoding job] [the specified path]; otherwise, <c>false</c>.</returns>
- public bool HasActiveTranscodingJob(string path, TranscodingJobType type)
- {
- return GetTranscodingJob(path, type) != null;
- }
-
- public TranscodingJob GetTranscodingJob(string path, TranscodingJobType type)
- {
- lock (_activeTranscodingJobs)
- {
- return _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
- }
- }
-
- public TranscodingJob GetTranscodingJob(string playSessionId)
- {
- lock (_activeTranscodingJobs)
- {
- return _activeTranscodingJobs.FirstOrDefault(j => string.Equals(j.PlaySessionId, playSessionId, StringComparison.OrdinalIgnoreCase));
- }
- }
-
- /// <summary>
- /// Called when [transcode begin request].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="type">The type.</param>
- public TranscodingJob OnTranscodeBeginRequest(string path, TranscodingJobType type)
- {
- lock (_activeTranscodingJobs)
- {
- var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
-
- if (job == null)
- {
- return null;
- }
-
- OnTranscodeBeginRequest(job);
-
- return job;
- }
- }
-
- public void OnTranscodeBeginRequest(TranscodingJob job)
- {
- job.ActiveRequestCount++;
-
- if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
- {
- job.StopKillTimer();
- }
- }
-
- public void OnTranscodeEndRequest(TranscodingJob job)
- {
- job.ActiveRequestCount--;
- //Logger.Debug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
- if (job.ActiveRequestCount <= 0)
- {
- PingTimer(job, false);
- }
- }
- internal void PingTranscodingJob(string playSessionId, bool? isUserPaused)
- {
- if (string.IsNullOrEmpty(playSessionId))
- {
- throw new ArgumentNullException("playSessionId");
- }
-
- //Logger.Debug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
-
- List<TranscodingJob> jobs;
-
- lock (_activeTranscodingJobs)
- {
- // This is really only needed for HLS.
- // Progressive streams can stop on their own reliably
- jobs = _activeTranscodingJobs.Where(j => string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)).ToList();
- }
-
- foreach (var job in jobs)
- {
- if (isUserPaused.HasValue)
- {
- //Logger.Debug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id);
- job.IsUserPaused = isUserPaused.Value;
- }
- PingTimer(job, true);
- }
- }
-
- private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
- {
- if (job.HasExited)
- {
- job.StopKillTimer();
- return;
- }
-
- var timerDuration = 10000;
-
- if (job.Type != TranscodingJobType.Progressive)
- {
- timerDuration = 60000;
- }
-
- job.PingTimeout = timerDuration;
- job.LastPingDate = DateTime.UtcNow;
-
- // Don't start the timer for playback checkins with progressive streaming
- if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
- {
- job.StartKillTimer(OnTranscodeKillTimerStopped);
- }
- else
- {
- job.ChangeKillTimerIfStarted();
- }
- }
-
- /// <summary>
- /// Called when [transcode kill timer stopped].
- /// </summary>
- /// <param name="state">The state.</param>
- private void OnTranscodeKillTimerStopped(object state)
- {
- var job = (TranscodingJob)state;
-
- if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
- {
- var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds;
-
- if (timeSinceLastPing < job.PingTimeout)
- {
- job.StartKillTimer(OnTranscodeKillTimerStopped, job.PingTimeout);
- return;
- }
- }
-
- Logger.Info("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
-
- KillTranscodingJob(job, true, path => true);
- }
-
- /// <summary>
- /// Kills the single transcoding job.
- /// </summary>
- /// <param name="deviceId">The device id.</param>
- /// <param name="playSessionId">The play session identifier.</param>
- /// <param name="deleteFiles">The delete files.</param>
- /// <returns>Task.</returns>
- internal void KillTranscodingJobs(string deviceId, string playSessionId, Func<string, bool> deleteFiles)
- {
- KillTranscodingJobs(j =>
- {
- if (!string.IsNullOrWhiteSpace(playSessionId))
- {
- return string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase);
- }
-
- return string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase);
-
- }, deleteFiles);
- }
-
- /// <summary>
- /// Kills the transcoding jobs.
- /// </summary>
- /// <param name="killJob">The kill job.</param>
- /// <param name="deleteFiles">The delete files.</param>
- /// <returns>Task.</returns>
- private void KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles)
- {
- var jobs = new List<TranscodingJob>();
-
- lock (_activeTranscodingJobs)
- {
- // This is really only needed for HLS.
- // Progressive streams can stop on their own reliably
- jobs.AddRange(_activeTranscodingJobs.Where(killJob));
- }
-
- if (jobs.Count == 0)
- {
- return;
- }
-
- foreach (var job in jobs)
- {
- KillTranscodingJob(job, false, deleteFiles);
- }
- }
-
- /// <summary>
- /// Kills the transcoding job.
- /// </summary>
- /// <param name="job">The job.</param>
- /// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param>
- /// <param name="delete">The delete.</param>
- private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
- {
- job.DisposeKillTimer();
-
- Logger.Debug("KillTranscodingJob - JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
-
- lock (_activeTranscodingJobs)
- {
- _activeTranscodingJobs.Remove(job);
-
- if (!job.CancellationTokenSource.IsCancellationRequested)
- {
- job.CancellationTokenSource.Cancel();
- }
- }
-
- lock (_transcodingLocks)
- {
- _transcodingLocks.Remove(job.Path);
- }
-
- lock (job.ProcessLock)
- {
- if (job.TranscodingThrottler != null)
- {
- job.TranscodingThrottler.Stop();
- }
-
- var process = job.Process;
-
- var hasExited = job.HasExited;
-
- if (!hasExited)
- {
- try
- {
- Logger.Info("Stopping ffmpeg process with q command for {0}", job.Path);
-
- //process.Kill();
- process.StandardInput.WriteLine("q");
-
- // Need to wait because killing is asynchronous
- if (!process.WaitForExit(5000))
- {
- Logger.Info("Killing ffmpeg process for {0}", job.Path);
- process.Kill();
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
- }
- }
- }
-
- if (delete(job.Path))
- {
- DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
- }
-
- if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error closing live stream for {0}", ex, job.Path);
- }
- }
- }
-
- private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
- {
- if (retryCount >= 10)
- {
- return;
- }
-
- Logger.Info("Deleting partial stream file(s) {0}", path);
-
- await Task.Delay(delayMs).ConfigureAwait(false);
-
- try
- {
- if (jobType == TranscodingJobType.Progressive)
- {
- DeleteProgressivePartialStreamFiles(path);
- }
- else
- {
- DeleteHlsPartialStreamFiles(path);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
- //Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
-
- DeletePartialStreamFiles(path, jobType, retryCount + 1, 500);
- }
- catch
- {
- //Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
- }
- }
-
- /// <summary>
- /// Deletes the progressive partial stream files.
- /// </summary>
- /// <param name="outputFilePath">The output file path.</param>
- private void DeleteProgressivePartialStreamFiles(string outputFilePath)
- {
- _fileSystem.DeleteFile(outputFilePath);
- }
-
- /// <summary>
- /// Deletes the HLS partial stream files.
- /// </summary>
- /// <param name="outputFilePath">The output file path.</param>
- private void DeleteHlsPartialStreamFiles(string outputFilePath)
- {
- var directory = _fileSystem.GetDirectoryName(outputFilePath);
- var name = Path.GetFileNameWithoutExtension(outputFilePath);
-
- var filesToDelete = _fileSystem.GetFilePaths(directory)
- .Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
- .ToList();
-
- Exception e = null;
-
- foreach (var file in filesToDelete)
- {
- try
- {
- //Logger.Debug("Deleting HLS file {0}", file);
- _fileSystem.DeleteFile(file);
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException ex)
- {
- e = ex;
- //Logger.ErrorException("Error deleting HLS file {0}", ex, file);
- }
- }
-
- if (e != null)
- {
- throw e;
- }
- }
- }
-
- /// <summary>
- /// Class TranscodingJob
- /// </summary>
- public class TranscodingJob
- {
- /// <summary>
- /// Gets or sets the play session identifier.
- /// </summary>
- /// <value>The play session identifier.</value>
- public string PlaySessionId { get; set; }
- /// <summary>
- /// Gets or sets the live stream identifier.
- /// </summary>
- /// <value>The live stream identifier.</value>
- public string LiveStreamId { get; set; }
-
- public bool IsLiveOutput { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public MediaSourceInfo MediaSource { get; set; }
- public string Path { get; set; }
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public TranscodingJobType Type { get; set; }
- /// <summary>
- /// Gets or sets the process.
- /// </summary>
- /// <value>The process.</value>
- public IProcess Process { get; set; }
- public ILogger Logger { get; private set; }
- /// <summary>
- /// Gets or sets the active request count.
- /// </summary>
- /// <value>The active request count.</value>
- public int ActiveRequestCount { get; set; }
- /// <summary>
- /// Gets or sets the kill timer.
- /// </summary>
- /// <value>The kill timer.</value>
- private ITimer KillTimer { get; set; }
-
- private readonly ITimerFactory _timerFactory;
-
- public string DeviceId { get; set; }
-
- public CancellationTokenSource CancellationTokenSource { get; set; }
-
- public object ProcessLock = new object();
-
- public bool HasExited { get; set; }
- public bool IsUserPaused { get; set; }
-
- public string Id { get; set; }
-
- public float? Framerate { get; set; }
- public double? CompletionPercentage { get; set; }
-
- public long? BytesDownloaded { get; set; }
- public long? BytesTranscoded { get; set; }
- public int? BitRate { get; set; }
-
- public long? TranscodingPositionTicks { get; set; }
- public long? DownloadPositionTicks { get; set; }
-
- public TranscodingThrottler TranscodingThrottler { get; set; }
-
- private readonly object _timerLock = new object();
-
- public DateTime LastPingDate { get; set; }
- public int PingTimeout { get; set; }
-
- public TranscodingJob(ILogger logger, ITimerFactory timerFactory)
- {
- Logger = logger;
- _timerFactory = timerFactory;
- }
-
- public void StopKillTimer()
- {
- lock (_timerLock)
- {
- if (KillTimer != null)
- {
- KillTimer.Change(Timeout.Infinite, Timeout.Infinite);
- }
- }
- }
-
- public void DisposeKillTimer()
- {
- lock (_timerLock)
- {
- if (KillTimer != null)
- {
- KillTimer.Dispose();
- KillTimer = null;
- }
- }
- }
-
- public void StartKillTimer(Action<object> callback)
- {
- StartKillTimer(callback, PingTimeout);
- }
-
- public void StartKillTimer(Action<object> callback, int intervalMs)
- {
- if (HasExited)
- {
- return;
- }
-
- lock (_timerLock)
- {
- if (KillTimer == null)
- {
- //Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
- KillTimer = _timerFactory.Create(callback, this, intervalMs, Timeout.Infinite);
- }
- else
- {
- //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
- KillTimer.Change(intervalMs, Timeout.Infinite);
- }
- }
- }
-
- public void ChangeKillTimerIfStarted()
- {
- if (HasExited)
- {
- return;
- }
-
- lock (_timerLock)
- {
- if (KillTimer != null)
- {
- var intervalMs = PingTimeout;
-
- //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
- KillTimer.Change(intervalMs, Timeout.Infinite);
- }
- }
}
}
}
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index a1f891506..41b17e535 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -61,9 +61,9 @@ namespace MediaBrowser.Api
user == null ? _libraryManager.RootFolder : user.RootFolder :
parentItem;
- var result = ((Folder)item).GetItems(GetItemsQuery(request, user));
+ var result = ((Folder)item).GetItemList(GetItemsQuery(request, user));
- return ToOptimizedResult(GetFilters(result.Items));
+ return ToOptimizedResult(GetFilters(result));
}
private QueryFilters GetFilters(BaseItem[] items)
diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs
index 2d161ccfd..0ce57a16a 100644
--- a/MediaBrowser.Api/GamesService.cs
+++ b/MediaBrowser.Api/GamesService.cs
@@ -12,6 +12,7 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
@@ -227,11 +228,13 @@ namespace MediaBrowser.Api
SimilarTo = item,
DtoOptions = dtoOptions
- }).ToList();
+ });
+
+ var returnList = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false));
var result = new QueryResult<BaseItemDto>
{
- Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
+ Items = returnList.ToArray(returnList.Count),
TotalRecordCount = itemsResult.Count
};
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index f6c97e091..318360336 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -279,13 +279,16 @@ namespace MediaBrowser.Api.Images
var itemImages = item.ImageInfos;
- foreach (var image in itemImages.Where(i => !item.AllowsMultipleImages(i.Type)))
+ foreach (var image in itemImages)
{
- var info = GetImageInfo(item, image, null);
-
- if (info != null)
+ if (!item.AllowsMultipleImages(image.Type))
{
- list.Add(info);
+ var info = GetImageInfo(item, image, null);
+
+ if (info != null)
+ {
+ list.Add(info);
+ }
}
}
@@ -312,7 +315,7 @@ namespace MediaBrowser.Api.Images
return list;
}
- private ImageInfo GetImageInfo(IHasImages item, ItemImageInfo info, int? imageIndex)
+ private ImageInfo GetImageInfo(IHasMetadata item, ItemImageInfo info, int? imageIndex)
{
try
{
@@ -507,7 +510,7 @@ namespace MediaBrowser.Api.Images
/// <param name="currentIndex">Index of the current.</param>
/// <param name="newIndex">The new index.</param>
/// <returns>Task.</returns>
- private Task UpdateItemIndex(IHasImages item, ImageType type, int currentIndex, int newIndex)
+ private Task UpdateItemIndex(IHasMetadata item, ImageType type, int currentIndex, int newIndex)
{
return item.SwapImages(type, currentIndex, newIndex);
}
@@ -520,7 +523,7 @@ namespace MediaBrowser.Api.Images
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
/// <returns>System.Object.</returns>
/// <exception cref="ResourceNotFoundException"></exception>
- public Task<object> GetImage(ImageRequest request, IHasImages item, bool isHeadRequest)
+ public Task<object> GetImage(ImageRequest request, IHasMetadata item, bool isHeadRequest)
{
if (request.PercentPlayed.HasValue)
{
@@ -603,7 +606,7 @@ namespace MediaBrowser.Api.Images
isHeadRequest);
}
- private async Task<object> GetImageResult(IHasImages item,
+ private async Task<object> GetImageResult(IHasMetadata item,
ImageRequest request,
ItemImageInfo image,
bool cropwhitespace,
@@ -749,7 +752,7 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- private ItemImageInfo GetImageInfo(ImageRequest request, IHasImages item)
+ private ItemImageInfo GetImageInfo(ImageRequest request, IHasMetadata item)
{
var index = request.Index ?? 0;
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 8c04e979e..313f7aab5 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -66,8 +66,8 @@ namespace MediaBrowser.Api
{
ParentalRatingOptions = _localizationManager.GetParentalRatings().ToList(),
ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToList(),
- Countries = _localizationManager.GetCountries().ToList(),
- Cultures = _localizationManager.GetCultures().ToList()
+ Countries = _localizationManager.GetCountries(),
+ Cultures = _localizationManager.GetCultures()
};
if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName) &&
@@ -242,7 +242,6 @@ namespace MediaBrowser.Api
item.CriticRating = request.CriticRating;
- item.DisplayMediaType = request.DisplayMediaType;
item.CommunityRating = request.CommunityRating;
item.HomePageUrl = request.HomePageUrl;
item.IndexNumber = request.IndexNumber;
@@ -268,11 +267,9 @@ namespace MediaBrowser.Api
item.Tagline = request.Taglines.FirstOrDefault();
}
- item.Keywords = request.Keywords;
-
if (request.Studios != null)
{
- item.Studios = request.Studios.Select(x => x.Name).ToList();
+ item.Studios = request.Studios.Select(x => x.Name).ToArray();
}
if (request.DateCreated.HasValue)
@@ -288,7 +285,7 @@ namespace MediaBrowser.Api
if (request.ProductionLocations != null)
{
- item.ProductionLocations = request.ProductionLocations.ToList();
+ item.ProductionLocations = request.ProductionLocations;
}
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
@@ -335,13 +332,6 @@ namespace MediaBrowser.Api
video.Video3DFormat = request.Video3DFormat;
}
- var game = item as Game;
-
- if (game != null)
- {
- game.PlayersSupported = request.Players;
- }
-
if (request.AlbumArtists != null)
{
var hasAlbumArtists = item as IHasAlbumArtist;
@@ -350,7 +340,7 @@ namespace MediaBrowser.Api
hasAlbumArtists.AlbumArtists = request
.AlbumArtists
.Select(i => i.Name)
- .ToList();
+ .ToArray();
}
}
@@ -382,8 +372,12 @@ namespace MediaBrowser.Api
if (series != null)
{
series.Status = GetSeriesStatus(request);
- series.AirDays = request.AirDays;
- series.AirTime = request.AirTime;
+
+ if (request.AirDays != null)
+ {
+ series.AirDays = request.AirDays;
+ series.AirTime = request.AirTime;
+ }
}
}
diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs
deleted file mode 100644
index ea610ac5c..000000000
--- a/MediaBrowser.Api/Library/FileOrganizationService.cs
+++ /dev/null
@@ -1,213 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Querying;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Library
-{
- [Route("/Library/FileOrganization", "GET", Summary = "Gets file organization results")]
- public class GetFileOrganizationActivity : IReturn<QueryResult<FileOrganizationResult>>
- {
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- /// <value>The start index.</value>
- [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? Limit { get; set; }
- }
-
- [Route("/Library/FileOrganizations", "DELETE", Summary = "Clears the activity log")]
- public class ClearOrganizationLog : IReturnVoid
- {
- }
-
- [Route("/Library/FileOrganizations/{Id}/File", "DELETE", Summary = "Deletes the original file of a organizer result")]
- public class DeleteOriginalFile : IReturnVoid
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Result Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Library/FileOrganizations/{Id}/Organize", "POST", Summary = "Performs an organization")]
- public class PerformOrganization : IReturn<QueryResult<FileOrganizationResult>>
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Result Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
- }
-
- [Route("/Library/FileOrganizations/{Id}/Episode/Organize", "POST", Summary = "Performs organization of a tv episode")]
- public class OrganizeEpisode
- {
- [ApiMember(Name = "Id", Description = "Result Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
-
- [ApiMember(Name = "SeriesId", Description = "Series Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string SeriesId { get; set; }
-
- [ApiMember(Name = "SeasonNumber", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int SeasonNumber { get; set; }
-
- [ApiMember(Name = "EpisodeNumber", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int EpisodeNumber { get; set; }
-
- [ApiMember(Name = "EndingEpisodeNumber", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? EndingEpisodeNumber { get; set; }
-
- [ApiMember(Name = "RememberCorrection", Description = "Whether or not to apply the same correction to future episodes of the same series.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
- public bool RememberCorrection { get; set; }
-
- [ApiMember(Name = "NewSeriesProviderIds", Description = "A list of provider IDs identifying a new series.", IsRequired = false, DataType = "Dictionary<string, string>", ParameterType = "query", Verb = "POST")]
- public Dictionary<string, string> NewSeriesProviderIds { get; set; }
-
- [ApiMember(Name = "NewSeriesName", Description = "Name of a series to add.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string NewSeriesName { get; set; }
-
- [ApiMember(Name = "NewSeriesYear", Description = "Year of a series to add.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string NewSeriesYear { get; set; }
-
- [ApiMember(Name = "TargetFolder", Description = "Target Folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string TargetFolder { get; set; }
- }
-
- [Route("/Library/FileOrganizations/SmartMatches", "GET", Summary = "Gets smart match entries")]
- public class GetSmartMatchInfos : IReturn<QueryResult<SmartMatchInfo>>
- {
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- /// <value>The start index.</value>
- [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? Limit { get; set; }
- }
-
- [Route("/Library/FileOrganizations/SmartMatches/Delete", "POST", Summary = "Deletes a smart match entry")]
- public class DeleteSmartMatchEntry
- {
- [ApiMember(Name = "Entries", Description = "SmartMatch Entry", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public List<NameValuePair> Entries { get; set; }
- }
-
- [Authenticated(Roles = "Admin")]
- public class FileOrganizationService : BaseApiService
- {
- private readonly IFileOrganizationService _iFileOrganizationService;
-
- private readonly IJsonSerializer _jsonSerializer;
-
- public FileOrganizationService(IFileOrganizationService iFileOrganizationService, IJsonSerializer jsonSerializer)
- {
- _iFileOrganizationService = iFileOrganizationService;
- _jsonSerializer = jsonSerializer;
- }
-
- public object Get(GetFileOrganizationActivity request)
- {
- var result = _iFileOrganizationService.GetResults(new FileOrganizationResultQuery
- {
- Limit = request.Limit,
- StartIndex = request.StartIndex
- });
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- public void Delete(DeleteOriginalFile request)
- {
- var task = _iFileOrganizationService.DeleteOriginalFile(request.Id);
-
- Task.WaitAll(task);
- }
-
- public void Delete(ClearOrganizationLog request)
- {
- var task = _iFileOrganizationService.ClearLog();
-
- Task.WaitAll(task);
- }
-
- public void Post(PerformOrganization request)
- {
- // Don't await this
- var task = _iFileOrganizationService.PerformOrganization(request.Id);
-
- // Async processing (close dialog early instead of waiting until the file has been copied)
- // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
- task.Wait(2000);
- }
-
- public void Post(OrganizeEpisode request)
- {
- var dicNewProviderIds = new Dictionary<string, string>();
-
- if (request.NewSeriesProviderIds != null)
- {
- dicNewProviderIds = request.NewSeriesProviderIds;
- }
-
- // Don't await this
- var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest
- {
- EndingEpisodeNumber = request.EndingEpisodeNumber,
- EpisodeNumber = request.EpisodeNumber,
- RememberCorrection = request.RememberCorrection,
- ResultId = request.Id,
- SeasonNumber = request.SeasonNumber,
- SeriesId = request.SeriesId,
- NewSeriesName = request.NewSeriesName,
- NewSeriesYear = request.NewSeriesYear,
- NewSeriesProviderIds = dicNewProviderIds,
- TargetFolder = request.TargetFolder
- });
-
- // Async processing (close dialog early instead of waiting until the file has been copied)
- // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
- task.Wait(2000);
- }
-
- public object Get(GetSmartMatchInfos request)
- {
- var result = _iFileOrganizationService.GetSmartMatchInfos(new FileOrganizationResultQuery
- {
- Limit = request.Limit,
- StartIndex = request.StartIndex
- });
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- public void Post(DeleteSmartMatchEntry request)
- {
- foreach (var entry in request.Entries)
- {
- _iFileOrganizationService.DeleteSmartMatchEntry(entry.Name, entry.Value);
- }
- }
- }
-}
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 3bb119cba..7dd1afaf4 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -29,6 +29,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.Library
{
@@ -460,22 +461,22 @@ namespace MediaBrowser.Api.Library
EnableImages = false
}
- }).ToArray();
+ });
if (!string.IsNullOrWhiteSpace(request.ImdbId))
{
- movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else if (!string.IsNullOrWhiteSpace(request.TmdbId))
{
- movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else
{
- movies = new BaseItem[] { };
+ movies = new List<BaseItem>();
}
- if (movies.Length > 0)
+ if (movies.Count > 0)
{
foreach (var item in movies)
{
@@ -732,7 +733,8 @@ namespace MediaBrowser.Api.Library
{
DeleteFileLocation = true
});
- }).ToArray();
+
+ }).ToArray(ids.Length);
Task.WaitAll(tasks);
}
@@ -758,7 +760,7 @@ namespace MediaBrowser.Api.Library
{
var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id));
- var reviewsArray = reviews.ToArray();
+ var reviewsArray = reviews.ToArray(reviews.Count);
var result = new QueryResult<ItemReview>
{
@@ -833,7 +835,7 @@ namespace MediaBrowser.Api.Library
throw new ResourceNotFoundException("Item not found.");
}
- while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
+ while (item.ThemeSongIds.Length == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
@@ -882,7 +884,7 @@ namespace MediaBrowser.Api.Library
throw new ResourceNotFoundException("Item not found.");
}
- while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
+ while (item.ThemeVideoIds.Length == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 837a0f6a6..09d4cdfa9 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -16,13 +16,12 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
-using MediaBrowser.Api.Playback.Progressive;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.LiveTv
{
@@ -734,7 +733,7 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
- return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, _environment, CancellationToken.None)
+ return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment, CancellationToken.None)
{
AllowEndOfFile = false
};
@@ -753,7 +752,7 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
- return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, _environment, CancellationToken.None)
+ return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment, CancellationToken.None)
{
AllowEndOfFile = false
};
@@ -921,7 +920,9 @@ namespace MediaBrowser.Api.LiveTv
options.AddCurrentProgram = request.AddCurrentProgram;
- var returnArray = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user)
+ .ConfigureAwait(false));
+ var returnArray = returnList.ToArray(returnList.Count);
var result = new QueryResult<BaseItemDto>
{
@@ -962,7 +963,7 @@ namespace MediaBrowser.Api.LiveTv
{
var query = new ProgramQuery
{
- ChannelIds = (request.ChannelIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(),
+ ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true),
UserId = request.UserId,
HasAired = request.HasAired,
EnableTotalRecordCount = request.EnableTotalRecordCount
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
index d84d889fa..20466c5f6 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
@@ -1,23 +1,19 @@
-using MediaBrowser.Model.Logging;
-using System;
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Net;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
-namespace MediaBrowser.Api.Playback.Progressive
+namespace MediaBrowser.Api.LiveTv
{
public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders
{
private readonly IFileSystem _fileSystem;
- private readonly TranscodingJob _job;
private readonly ILogger _logger;
private readonly string _path;
private readonly CancellationToken _cancellationToken;
@@ -32,22 +28,20 @@ namespace MediaBrowser.Api.Playback.Progressive
private readonly IDirectStreamProvider _directStreamProvider;
private readonly IEnvironmentInfo _environment;
- public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
+ public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
{
_fileSystem = fileSystem;
_path = path;
_outputHeaders = outputHeaders;
- _job = job;
_logger = logger;
_cancellationToken = cancellationToken;
_environment = environment;
}
- public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
+ public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
{
_directStreamProvider = directStreamProvider;
_outputHeaders = outputHeaders;
- _job = job;
_logger = logger;
_cancellationToken = cancellationToken;
_environment = environment;
@@ -77,61 +71,48 @@ namespace MediaBrowser.Api.Playback.Progressive
{
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token;
- try
+ if (_directStreamProvider != null)
{
- if (_directStreamProvider != null)
- {
- await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
- return;
- }
+ await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
+ return;
+ }
- var eofCount = 0;
+ var eofCount = 0;
- // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
- var allowAsyncFileRead = _environment.OperatingSystem != OperatingSystem.Windows;
+ // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+ var allowAsyncFileRead = _environment.OperatingSystem != OperatingSystem.Windows;
- using (var inputStream = GetInputStream(allowAsyncFileRead))
+ using (var inputStream = GetInputStream(allowAsyncFileRead))
+ {
+ if (StartPosition > 0)
{
- if (StartPosition > 0)
+ inputStream.Position = StartPosition;
+ }
+
+ while (eofCount < 20 || !AllowEndOfFile)
+ {
+ int bytesRead;
+ if (allowAsyncFileRead)
+ {
+ bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
+ }
+ else
{
- inputStream.Position = StartPosition;
+ bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
}
- while (eofCount < 20 || !AllowEndOfFile)
+ //var position = fs.Position;
+ //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+
+ if (bytesRead == 0)
{
- int bytesRead;
- if (allowAsyncFileRead)
- {
- bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
- }
-
- //var position = fs.Position;
- //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
-
- if (bytesRead == 0)
- {
- if (_job == null || _job.HasExited)
- {
- eofCount++;
- }
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- eofCount = 0;
- }
+ eofCount++;
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ eofCount = 0;
}
- }
- }
- finally
- {
- if (_job != null)
- {
- ApiEntryPoint.Instance.OnTranscodeEndRequest(_job);
}
}
}
@@ -152,11 +133,6 @@ namespace MediaBrowser.Api.Playback.Progressive
_bytesWritten += bytesRead;
totalBytesRead += bytesRead;
-
- if (_job != null)
- {
- _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
- }
}
}
@@ -179,11 +155,6 @@ namespace MediaBrowser.Api.Playback.Progressive
_bytesWritten += bytesRead;
totalBytesRead += bytesRead;
-
- if (_job != null)
- {
- _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
- }
}
}
diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs
index 90b963149..eee340a87 100644
--- a/MediaBrowser.Api/LocalizationService.cs
+++ b/MediaBrowser.Api/LocalizationService.cs
@@ -85,7 +85,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetCountries request)
{
- var result = _localization.GetCountries().ToList();
+ var result = _localization.GetCountries();
return ToOptimizedResult(result);
}
@@ -97,7 +97,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetCultures request)
{
- var result = _localization.GetCultures().ToList();
+ var result = _localization.GetCultures();
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index a798ab5ff..810b0f6b2 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -48,9 +48,7 @@
<Compile Include="Dlna\DlnaService.cs" />
<Compile Include="FilterService.cs" />
<Compile Include="IHasDtoOptions.cs" />
- <Compile Include="Playback\MediaInfoService.cs" />
- <Compile Include="Playback\TranscodingThrottler.cs" />
- <Compile Include="Playback\UniversalAudioService.cs" />
+ <Compile Include="LiveTv\ProgressiveFileCopier.cs" />
<Compile Include="PlaylistService.cs" />
<Compile Include="Reports\Activities\ReportActivitiesBuilder.cs" />
<Compile Include="Reports\Common\HeaderActivitiesMetadata.cs" />
@@ -93,7 +91,6 @@
<Compile Include="ItemRefreshService.cs" />
<Compile Include="ItemUpdateService.cs" />
<Compile Include="Library\LibraryService.cs" />
- <Compile Include="Library\FileOrganizationService.cs" />
<Compile Include="Library\LibraryStructureService.cs" />
<Compile Include="LiveTv\LiveTvService.cs" />
<Compile Include="LocalizationService.cs" />
@@ -102,18 +99,6 @@
<Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" />
<Compile Include="PackageService.cs" />
- <Compile Include="Playback\Hls\BaseHlsService.cs" />
- <Compile Include="Playback\Hls\DynamicHlsService.cs" />
- <Compile Include="Playback\Hls\HlsSegmentService.cs" />
- <Compile Include="Playback\Hls\VideoHlsService.cs" />
- <Compile Include="Playback\Progressive\AudioService.cs" />
- <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
- <Compile Include="Playback\BaseStreamingService.cs" />
- <Compile Include="Playback\Progressive\ProgressiveStreamWriter.cs" />
- <Compile Include="Playback\StaticRemoteStreamWriter.cs" />
- <Compile Include="Playback\StreamRequest.cs" />
- <Compile Include="Playback\StreamState.cs" />
- <Compile Include="Playback\Progressive\VideoService.cs" />
<Compile Include="PluginService.cs" />
<Compile Include="Images\RemoteImageService.cs" />
<Compile Include="ScheduledTasks\ScheduledTaskService.cs" />
@@ -127,7 +112,6 @@
<Compile Include="System\ActivityLogWebSocketListener.cs" />
<Compile Include="System\SystemService.cs" />
<Compile Include="Movies\TrailersService.cs" />
- <Compile Include="TestService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
@@ -137,7 +121,6 @@
<Compile Include="UserLibrary\ItemsService.cs" />
<Compile Include="UserLibrary\MusicGenresService.cs" />
<Compile Include="UserLibrary\PersonsService.cs" />
- <Compile Include="UserLibrary\PlaystateService.cs" />
<Compile Include="UserLibrary\StudiosService.cs" />
<Compile Include="UserLibrary\UserLibraryService.cs" />
<Compile Include="UserLibrary\UserViewsService.cs" />
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index e20fa2cca..c668d2c75 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -158,17 +158,19 @@ namespace MediaBrowser.Api.Movies
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = request.Limit,
- IncludeItemTypes = itemTypes.ToArray(),
+ IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
SimilarTo = item,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).ToList();
+ });
+
+ var returnList = await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false);
var result = new QueryResult<BaseItemDto>
{
- Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
+ Items = returnList.ToArray(returnList.Count),
TotalRecordCount = itemsResult.Count
};
@@ -200,7 +202,7 @@ namespace MediaBrowser.Api.Movies
DtoOptions = dtoOptions
};
- var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();
+ var recentlyPlayedMovies = _libraryManager.GetItemList(query);
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
@@ -211,19 +213,19 @@ namespace MediaBrowser.Api.Movies
var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- IncludeItemTypes = itemTypes.ToArray(),
+ IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
SortBy = new[] { ItemSortBy.Random },
SortOrder = SortOrder.Descending,
Limit = 10,
IsFavoriteOrLiked = true,
- ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(),
+ ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(recentlyPlayedMovies.Count),
EnableGroupByMetadataKey = true,
ParentId = parentIdGuid,
Recursive = true,
DtoOptions = dtoOptions
- }).ToList();
+ });
var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
// Get recently played directors
@@ -300,7 +302,7 @@ namespace MediaBrowser.Api.Movies
// Account for duplicates by imdb id, since the database doesn't support this yet
Limit = itemLimit + 2,
PersonTypes = new[] { PersonType.Director },
- IncludeItemTypes = itemTypes.ToArray(),
+ IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
@@ -311,12 +313,14 @@ namespace MediaBrowser.Api.Movies
if (items.Count > 0)
{
+ var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user).Result;
+
yield return new RecommendationDto
{
BaselineItemName = name,
CategoryId = name.GetMD5().ToString("N"),
RecommendationType = type,
- Items = _dtoService.GetBaseItemDtos(items, dtoOptions, user).Result.ToArray()
+ Items = returnItems.ToArray(returnItems.Count)
};
}
}
@@ -338,7 +342,7 @@ namespace MediaBrowser.Api.Movies
Person = name,
// Account for duplicates by imdb id, since the database doesn't support this yet
Limit = itemLimit + 2,
- IncludeItemTypes = itemTypes.ToArray(),
+ IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
@@ -349,12 +353,14 @@ namespace MediaBrowser.Api.Movies
if (items.Count > 0)
{
+ var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user).Result;
+
yield return new RecommendationDto
{
BaselineItemName = name,
CategoryId = name.GetMD5().ToString("N"),
RecommendationType = type,
- Items = _dtoService.GetBaseItemDtos(items, dtoOptions, user).Result.ToArray()
+ Items = returnItems.ToArray(returnItems.Count)
};
}
}
@@ -374,28 +380,30 @@ namespace MediaBrowser.Api.Movies
var similar = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = itemLimit,
- IncludeItemTypes = itemTypes.ToArray(),
+ IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
SimilarTo = item,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).ToList();
+ });
if (similar.Count > 0)
{
+ var returnItems = _dtoService.GetBaseItemDtos(similar, dtoOptions, user).Result;
+
yield return new RecommendationDto
{
BaselineItemName = item.Name,
CategoryId = item.Id.ToString("N"),
RecommendationType = type,
- Items = _dtoService.GetBaseItemDtos(similar, dtoOptions, user).Result.ToArray()
+ Items = returnItems.ToArray(returnItems.Count)
};
}
}
}
- private IEnumerable<string> GetActors(IEnumerable<BaseItem> items)
+ private IEnumerable<string> GetActors(List<BaseItem> items)
{
var people = _libraryManager.GetPeople(new InternalPeopleQuery
{
@@ -414,7 +422,7 @@ namespace MediaBrowser.Api.Movies
.DistinctNames();
}
- private IEnumerable<string> GetDirectors(IEnumerable<BaseItem> items)
+ private IEnumerable<string> GetDirectors(List<BaseItem> items)
{
var people = _libraryManager.GetPeople(new InternalPeopleQuery
{
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index 3cb29de07..b0f086ec5 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.Music
{
@@ -180,16 +181,19 @@ namespace MediaBrowser.Api.Music
return GetResult(items, user, request, dtoOptions);
}
- private async Task<object> GetResult(IEnumerable<Audio> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
+ private async Task<object> GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
{
- var list = items.ToList();
+ var list = items;
var result = new ItemsResult
{
TotalRecordCount = list.Count
};
- result.Items = (await _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user)
+ .ConfigureAwait(false));
+
+ result.Items = returnList.ToArray(returnList.Count);
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
deleted file mode 100644
index c300fcce3..000000000
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ /dev/null
@@ -1,1024 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-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;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
-
-namespace MediaBrowser.Api.Playback
-{
- /// <summary>
- /// Class BaseStreamingService
- /// </summary>
- public abstract class BaseStreamingService : BaseApiService
- {
- /// <summary>
- /// Gets or sets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
- protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- protected IUserManager UserManager { get; private set; }
-
- /// <summary>
- /// Gets or sets the library manager.
- /// </summary>
- /// <value>The library manager.</value>
- protected ILibraryManager LibraryManager { get; private set; }
-
- /// <summary>
- /// Gets or sets the iso manager.
- /// </summary>
- /// <value>The iso manager.</value>
- protected IIsoManager IsoManager { get; private set; }
-
- /// <summary>
- /// Gets or sets the media encoder.
- /// </summary>
- /// <value>The media encoder.</value>
- protected IMediaEncoder MediaEncoder { get; private set; }
-
- protected IFileSystem FileSystem { get; private set; }
-
- protected IDlnaManager DlnaManager { get; private set; }
- protected IDeviceManager DeviceManager { get; private set; }
- protected ISubtitleEncoder SubtitleEncoder { get; private set; }
- protected IMediaSourceManager MediaSourceManager { get; private set; }
- protected IZipClient ZipClient { get; private set; }
- protected IJsonSerializer JsonSerializer { get; private set; }
-
- public static IServerApplicationHost AppHost;
- public static IHttpClient HttpClient;
- protected IAuthorizationContext AuthorizationContext { get; private set; }
-
- protected EncodingHelper EncodingHelper { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
- /// </summary>
- protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext)
- {
- JsonSerializer = jsonSerializer;
- AuthorizationContext = authorizationContext;
- ZipClient = zipClient;
- MediaSourceManager = mediaSourceManager;
- DeviceManager = deviceManager;
- SubtitleEncoder = subtitleEncoder;
- DlnaManager = dlnaManager;
- FileSystem = fileSystem;
- ServerConfigurationManager = serverConfig;
- UserManager = userManager;
- LibraryManager = libraryManager;
- IsoManager = isoManager;
- MediaEncoder = mediaEncoder;
- EncodingHelper = new EncodingHelper(MediaEncoder, serverConfig, FileSystem, SubtitleEncoder);
- }
-
- /// <summary>
- /// Gets the command line arguments.
- /// </summary>
- protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
-
- /// <summary>
- /// Gets the type of the transcoding job.
- /// </summary>
- /// <value>The type of the transcoding job.</value>
- protected abstract TranscodingJobType TranscodingJobType { get; }
-
- /// <summary>
- /// Gets the output file extension.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected virtual string GetOutputFileExtension(StreamState state)
- {
- return Path.GetExtension(state.RequestedUrl);
- }
-
- /// <summary>
- /// Gets the output file path.
- /// </summary>
- private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
- {
- var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
-
- var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
-
- data += "-" + (state.Request.DeviceId ?? string.Empty);
- data += "-" + (state.Request.PlaySessionId ?? string.Empty);
-
- var dataHash = data.GetMD5().ToString("N");
-
- if (EnableOutputInSubFolder)
- {
- return Path.Combine(folder, dataHash, dataHash + (outputFileExtension ?? string.Empty).ToLower());
- }
-
- return Path.Combine(folder, dataHash + (outputFileExtension ?? string.Empty).ToLower());
- }
-
- protected virtual bool EnableOutputInSubFolder
- {
- get { return false; }
- }
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- protected virtual string GetDefaultH264Preset()
- {
- return "superfast";
- }
-
- private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
- {
- if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
- {
- state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
- }
-
- if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
- {
- var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
- {
- OpenToken = state.MediaSource.OpenToken
-
- }, cancellationTokenSource.Token).ConfigureAwait(false);
-
- EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
-
- if (state.VideoRequest != null)
- {
- EncodingHelper.TryStreamCopy(state);
- }
- }
-
- if (state.MediaSource.BufferMs.HasValue)
- {
- await Task.Delay(state.MediaSource.BufferMs.Value, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Starts the FFMPEG.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="cancellationTokenSource">The cancellation token source.</param>
- /// <param name="workingDirectory">The working directory.</param>
- /// <returns>Task.</returns>
- protected async Task<TranscodingJob> StartFfMpeg(StreamState state,
- string outputPath,
- CancellationTokenSource cancellationTokenSource,
- string workingDirectory = null)
- {
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath));
-
- await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
-
- if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- if (!string.IsNullOrWhiteSpace(auth.UserId))
- {
- var user = UserManager.GetUserById(auth.UserId);
- if (!user.Policy.EnableVideoPlaybackTranscoding)
- {
- ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
-
- throw new ArgumentException("User does not have access to video transcoding");
- }
- }
- }
-
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
- var transcodingId = Guid.NewGuid().ToString("N");
- var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true);
-
- var process = ApiEntryPoint.Instance.ProcessFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
-
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
-
- IsHidden = true,
- ErrorDialog = false,
- EnableRaisingEvents = true,
- WorkingDirectory = !string.IsNullOrWhiteSpace(workingDirectory) ? workingDirectory : null
- });
-
- var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
- state.Request.PlaySessionId,
- state.MediaSource.LiveStreamId,
- transcodingId,
- TranscodingJobType,
- process,
- state.Request.DeviceId,
- state,
- cancellationTokenSource);
-
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- Logger.Info(commandLineLogMessage);
-
- var logFilePrefix = "ffmpeg-transcode";
- if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- logFilePrefix = "ffmpeg-directstream";
- }
- else if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- logFilePrefix = "ffmpeg-remux";
- }
-
- var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(logFilePath));
-
- // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, 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 state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
-
- process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error starting ffmpeg", ex);
-
- ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
-
- throw;
- }
-
- // MUST read both stdout and stderr asynchronously or a deadlock may occurr
- //process.BeginOutputReadLine();
-
- state.TranscodingJob = transcodingJob;
-
- // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);
-
- // Wait for the file to exist before proceeeding
- while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
- {
- await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
- }
-
- if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
- {
- await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
-
- if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited)
- {
- await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- }
-
- if (!transcodingJob.HasExited)
- {
- StartThrottler(state, transcodingJob);
- }
-
- return transcodingJob;
- }
-
- private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
- {
- if (EnableThrottling(state))
- {
- transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager, ApiEntryPoint.Instance.TimerFactory, FileSystem);
- state.TranscodingThrottler.Start();
- }
- }
-
- private bool EnableThrottling(StreamState state)
- {
- 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>
- /// Processes the exited.
- /// </summary>
- /// <param name="process">The process.</param>
- /// <param name="job">The job.</param>
- /// <param name="state">The state.</param>
- private void OnFfMpegProcessExited(IProcess process, TranscodingJob job, StreamState state)
- {
- if (job != null)
- {
- job.HasExited = true;
- }
-
- Logger.Debug("Disposing stream resources");
- state.Dispose();
-
- try
- {
- Logger.Info("FFMpeg exited with code {0}", process.ExitCode);
- }
- catch
- {
- Logger.Error("FFMpeg exited with an error.");
- }
-
- // This causes on exited to be called twice:
- //try
- //{
- // // Dispose the process
- // process.Dispose();
- //}
- //catch (Exception ex)
- //{
- // Logger.ErrorException("Error disposing ffmpeg.", ex);
- //}
- }
-
- /// <summary>
- /// Parses the parameters.
- /// </summary>
- /// <param name="request">The request.</param>
- private void ParseParams(StreamRequest request)
- {
- var vals = request.Params.Split(';');
-
- var videoRequest = request as VideoStreamRequest;
-
- for (var i = 0; i < vals.Length; i++)
- {
- var val = vals[i];
-
- if (string.IsNullOrWhiteSpace(val))
- {
- continue;
- }
-
- if (i == 0)
- {
- request.DeviceProfileId = val;
- }
- else if (i == 1)
- {
- request.DeviceId = val;
- }
- else if (i == 2)
- {
- request.MediaSourceId = val;
- }
- else if (i == 3)
- {
- request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- else if (i == 4)
- {
- if (videoRequest != null)
- {
- videoRequest.VideoCodec = val;
- }
- }
- else if (i == 5)
- {
- request.AudioCodec = val;
- }
- else if (i == 6)
- {
- if (videoRequest != null)
- {
- videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
- }
- }
- else if (i == 7)
- {
- if (videoRequest != null)
- {
- videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
- }
- }
- else if (i == 8)
- {
- if (videoRequest != null)
- {
- videoRequest.VideoBitRate = int.Parse(val, UsCulture);
- }
- }
- else if (i == 9)
- {
- request.AudioBitRate = int.Parse(val, UsCulture);
- }
- else if (i == 10)
- {
- request.MaxAudioChannels = int.Parse(val, UsCulture);
- }
- else if (i == 11)
- {
- if (videoRequest != null)
- {
- videoRequest.MaxFramerate = float.Parse(val, UsCulture);
- }
- }
- else if (i == 12)
- {
- if (videoRequest != null)
- {
- videoRequest.MaxWidth = int.Parse(val, UsCulture);
- }
- }
- else if (i == 13)
- {
- if (videoRequest != null)
- {
- videoRequest.MaxHeight = int.Parse(val, UsCulture);
- }
- }
- else if (i == 14)
- {
- request.StartTimeTicks = long.Parse(val, UsCulture);
- }
- else if (i == 15)
- {
- if (videoRequest != null)
- {
- videoRequest.Level = val;
- }
- }
- else if (i == 16)
- {
- if (videoRequest != null)
- {
- videoRequest.MaxRefFrames = int.Parse(val, UsCulture);
- }
- }
- else if (i == 17)
- {
- if (videoRequest != null)
- {
- videoRequest.MaxVideoBitDepth = int.Parse(val, UsCulture);
- }
- }
- else if (i == 18)
- {
- if (videoRequest != null)
- {
- videoRequest.Profile = val;
- }
- }
- else if (i == 19)
- {
- // cabac no longer used
- }
- else if (i == 20)
- {
- request.PlaySessionId = val;
- }
- else if (i == 21)
- {
- // api_key
- }
- else if (i == 22)
- {
- request.LiveStreamId = val;
- }
- else if (i == 23)
- {
- // Duplicating ItemId because of MediaMonkey
- }
- else if (i == 24)
- {
- if (videoRequest != null)
- {
- videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 25)
- {
- if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
- {
- SubtitleDeliveryMethod method;
- if (Enum.TryParse(val, out method))
- {
- videoRequest.SubtitleMethod = method;
- }
- }
- }
- else if (i == 26)
- {
- request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
- }
- else if (i == 27)
- {
- if (videoRequest != null)
- {
- videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 28)
- {
- request.Tag = val;
- }
- else if (i == 29)
- {
- if (videoRequest != null)
- {
- videoRequest.RequireAvc = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 30)
- {
- request.SubtitleCodec = val;
- }
- else if (i == 31)
- {
- if (videoRequest != null)
- {
- videoRequest.RequireNonAnamorphic = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 32)
- {
- if (videoRequest != null)
- {
- videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 33)
- {
- request.TranscodeReasons = val;
- }
- }
- }
-
- /// <summary>
- /// Parses the dlna headers.
- /// </summary>
- /// <param name="request">The request.</param>
- private void ParseDlnaHeaders(StreamRequest request)
- {
- if (!request.StartTimeTicks.HasValue)
- {
- var timeSeek = GetHeader("TimeSeekRange.dlna.org");
-
- request.StartTimeTicks = ParseTimeSeekHeader(timeSeek);
- }
- }
-
- /// <summary>
- /// Parses the time seek header.
- /// </summary>
- private long? ParseTimeSeekHeader(string value)
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- return null;
- }
-
- if (value.IndexOf("npt=", StringComparison.OrdinalIgnoreCase) != 0)
- {
- throw new ArgumentException("Invalid timeseek header");
- }
- value = value.Substring(4).Split(new[] { '-' }, 2)[0];
-
- if (value.IndexOf(':') == -1)
- {
- // Parses npt times in the format of '417.33'
- double seconds;
- if (double.TryParse(value, NumberStyles.Any, UsCulture, out seconds))
- {
- return TimeSpan.FromSeconds(seconds).Ticks;
- }
-
- throw new ArgumentException("Invalid timeseek header");
- }
-
- // Parses npt times in the format of '10:19:25.7'
- var tokens = value.Split(new[] { ':' }, 3);
- double secondsSum = 0;
- var timeFactor = 3600;
-
- foreach (var time in tokens)
- {
- double digit;
- if (double.TryParse(time, NumberStyles.Any, UsCulture, out digit))
- {
- secondsSum += digit * timeFactor;
- }
- else
- {
- throw new ArgumentException("Invalid timeseek header");
- }
- timeFactor /= 60;
- }
- return TimeSpan.FromSeconds(secondsSum).Ticks;
- }
-
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>StreamState.</returns>
- protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
- {
- ParseDlnaHeaders(request);
-
- if (!string.IsNullOrWhiteSpace(request.Params))
- {
- ParseParams(request);
- }
-
- var url = Request.PathInfo;
-
- if (string.IsNullOrEmpty(request.AudioCodec))
- {
- request.AudioCodec = EncodingHelper.InferAudioCodec(url);
- }
-
- var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
- string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
-
- var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
- {
- Request = request,
- RequestedUrl = url,
- UserAgent = Request.UserAgent,
- EnableDlnaHeaders = enableDlnaHeaders
- };
-
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- if (!string.IsNullOrWhiteSpace(auth.UserId))
- {
- state.User = UserManager.GetUserById(auth.UserId);
- }
-
- //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
- // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
- // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
- //{
- // state.SegmentLength = 6;
- //}
-
- if (state.VideoRequest != null)
- {
- if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
- {
- state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
- }
- }
-
- if (!string.IsNullOrWhiteSpace(request.AudioCodec))
- {
- state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i))
- ?? state.SupportedAudioCodecs.FirstOrDefault();
- }
-
- if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
- {
- state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i))
- ?? state.SupportedSubtitleCodecs.FirstOrDefault();
- }
-
- var item = LibraryManager.GetItemById(request.Id);
-
- state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
-
- //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
- // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
- //if (primaryImage != null)
- //{
- // state.AlbumCoverPath = primaryImage.Path;
- //}
-
- MediaSourceInfo mediaSource = null;
- if (string.IsNullOrWhiteSpace(request.LiveStreamId))
- {
- TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
- ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
- : null;
-
- if (currentJob != null)
- {
- mediaSource = currentJob.MediaSource;
- }
-
- if (mediaSource == null)
- {
- var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
-
- mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
- ? mediaSources.First()
- : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
-
- if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
- {
- mediaSource = mediaSources.First();
- }
- }
- }
- else
- {
- var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
- mediaSource = liveStreamInfo.Item1;
- state.DirectStreamProvider = liveStreamInfo.Item2;
- }
-
- var videoRequest = request as VideoStreamRequest;
-
- EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url);
-
- var container = Path.GetExtension(state.RequestedUrl);
-
- if (string.IsNullOrEmpty(container))
- {
- container = request.Container;
- }
-
- if (string.IsNullOrEmpty(container))
- {
- container = request.Static ?
- state.InputContainer :
- GetOutputFileExtension(state);
- }
-
- state.OutputContainer = (container ?? string.Empty).TrimStart('.');
-
- state.OutputAudioBitrate = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream);
-
- state.OutputAudioCodec = state.Request.AudioCodec;
-
- state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec);
-
- if (videoRequest != null)
- {
- state.OutputVideoCodec = state.VideoRequest.VideoCodec;
- state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
-
- if (videoRequest != null)
- {
- EncodingHelper.TryStreamCopy(state);
- }
-
- if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var resolution = ResolutionNormalizer.Normalize(
- state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
- state.OutputVideoBitrate.Value,
- state.VideoStream == null ? null : state.VideoStream.Codec,
- state.OutputVideoCodec,
- videoRequest.MaxWidth,
- videoRequest.MaxHeight);
-
- videoRequest.MaxWidth = resolution.MaxWidth;
- videoRequest.MaxHeight = resolution.MaxHeight;
- }
-
- ApplyDeviceProfileSettings(state);
- }
- else
- {
- ApplyDeviceProfileSettings(state);
- }
-
- var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
- ? GetOutputFileExtension(state)
- : ("." + state.OutputContainer);
-
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
- state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext);
-
- return state;
- }
-
- private void ApplyDeviceProfileSettings(StreamState state)
- {
- var headers = Request.Headers.ToDictionary();
-
- if (!string.IsNullOrWhiteSpace(state.Request.DeviceProfileId))
- {
- state.DeviceProfile = DlnaManager.GetProfile(state.Request.DeviceProfileId);
- }
- else
- {
- if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
- {
- var caps = DeviceManager.GetCapabilities(state.Request.DeviceId);
-
- if (caps != null)
- {
- state.DeviceProfile = caps.DeviceProfile;
- }
- else
- {
- state.DeviceProfile = DlnaManager.GetProfile(headers);
- }
- }
- }
-
- var profile = state.DeviceProfile;
-
- if (profile == null)
- {
- // Don't use settings from the default profile.
- // Only use a specific profile if it was requested.
- return;
- }
-
- var audioCodec = state.ActualOutputAudioCodec;
- var videoCodec = state.ActualOutputVideoCodec;
-
- var mediaProfile = state.VideoRequest == null ?
- profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate, state.OutputAudioSampleRate, state.OutputAudioBitDepth) :
- profile.GetVideoMediaProfile(state.OutputContainer,
- audioCodec,
- videoCodec,
- state.OutputWidth,
- state.OutputHeight,
- state.TargetVideoBitDepth,
- state.OutputVideoBitrate,
- state.TargetVideoProfile,
- state.TargetVideoLevel,
- state.TargetFramerate,
- state.TargetPacketLength,
- state.TargetTimestamp,
- state.IsTargetAnamorphic,
- state.IsTargetInterlaced,
- state.TargetRefFrames,
- state.TargetVideoStreamCount,
- state.TargetAudioStreamCount,
- state.TargetVideoCodecTag,
- state.IsTargetAVC);
-
- if (mediaProfile != null)
- {
- state.MimeType = mediaProfile.MimeType;
- }
-
- if (!state.Request.Static)
- {
- var transcodingProfile = state.VideoRequest == null ?
- profile.GetAudioTranscodingProfile(state.OutputContainer, audioCodec) :
- profile.GetVideoTranscodingProfile(state.OutputContainer, audioCodec, videoCodec);
-
- if (transcodingProfile != null)
- {
- state.EstimateContentLength = transcodingProfile.EstimateContentLength;
- state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
- state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
- if (state.VideoRequest != null)
- {
- state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
- state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
- }
- }
- }
- }
-
- /// <summary>
- /// Adds the dlna headers.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <param name="isStaticallyStreamed">if set to <c>true</c> [is statically streamed].</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- protected void AddDlnaHeaders(StreamState state, IDictionary<string, string> responseHeaders, bool isStaticallyStreamed)
- {
- if (!state.EnableDlnaHeaders)
- {
- return;
- }
-
- var profile = state.DeviceProfile;
-
- var transferMode = GetHeader("transferMode.dlna.org");
- responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
- responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
-
- if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase))
- {
- if (state.RunTimeTicks.HasValue)
- {
- var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
- responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture));
- }
- }
-
- if (state.RunTimeTicks.HasValue && !isStaticallyStreamed && profile != null)
- {
- AddTimeSeekResponseHeaders(state, responseHeaders);
- }
-
- if (profile == null)
- {
- profile = DlnaManager.GetDefaultProfile();
- }
-
- var audioCodec = state.ActualOutputAudioCodec;
-
- if (state.VideoRequest == null)
- {
- responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile)
- .BuildAudioHeader(
- state.OutputContainer,
- audioCodec,
- state.OutputAudioBitrate,
- state.OutputAudioSampleRate,
- state.OutputAudioChannels,
- state.OutputAudioBitDepth,
- isStaticallyStreamed,
- state.RunTimeTicks,
- state.TranscodeSeekInfo
- );
- }
- else
- {
- var videoCodec = state.ActualOutputVideoCodec;
-
- responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile)
- .BuildVideoHeader(
- state.OutputContainer,
- videoCodec,
- audioCodec,
- state.OutputWidth,
- state.OutputHeight,
- state.TargetVideoBitDepth,
- state.OutputVideoBitrate,
- state.TargetTimestamp,
- isStaticallyStreamed,
- state.RunTimeTicks,
- state.TargetVideoProfile,
- state.TargetVideoLevel,
- state.TargetFramerate,
- state.TargetPacketLength,
- state.TranscodeSeekInfo,
- state.IsTargetAnamorphic,
- state.IsTargetInterlaced,
- state.TargetRefFrames,
- state.TargetVideoStreamCount,
- state.TargetAudioStreamCount,
- state.TargetVideoCodecTag,
- state.IsTargetAVC
-
- ).FirstOrDefault() ?? string.Empty;
- }
-
- foreach (var item in responseHeaders)
- {
- Request.Response.AddHeader(item.Key, item.Value);
- }
- }
-
- private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
- {
- var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);
- var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(UsCulture);
-
- responseHeaders["TimeSeekRange.dlna.org"] = string.Format("npt={0}-{1}/{1}", startSeconds, runtimeSeconds);
- responseHeaders["X-AvailableSeekRange"] = string.Format("1 npt={0}-{1}", startSeconds, runtimeSeconds);
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
deleted file mode 100644
index 83157c703..000000000
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ /dev/null
@@ -1,331 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Api.Playback.Hls
-{
- /// <summary>
- /// Class BaseHlsService
- /// </summary>
- public abstract class BaseHlsService : BaseStreamingService
- {
- /// <summary>
- /// Gets the audio arguments.
- /// </summary>
- protected abstract string GetAudioArguments(StreamState state, EncodingOptions encodingOptions);
-
- /// <summary>
- /// Gets the video arguments.
- /// </summary>
- protected abstract string GetVideoArguments(StreamState state, EncodingOptions encodingOptions);
-
- /// <summary>
- /// Gets the segment file extension.
- /// </summary>
- protected string GetSegmentFileExtension(StreamRequest request)
- {
- var segmentContainer = request.SegmentContainer;
- if (!string.IsNullOrWhiteSpace(segmentContainer))
- {
- return "." + segmentContainer;
- }
-
- return ".ts";
- }
-
- /// <summary>
- /// Gets the type of the transcoding job.
- /// </summary>
- /// <value>The type of the transcoding job.</value>
- protected override TranscodingJobType TranscodingJobType
- {
- get { return TranscodingJobType.Hls; }
- }
-
- /// <summary>
- /// Processes the request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="isLive">if set to <c>true</c> [is live].</param>
- /// <returns>System.Object.</returns>
- protected async Task<object> ProcessRequest(StreamRequest request, bool isLive)
- {
- return await ProcessRequestAsync(request, isLive).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Processes the request async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="isLive">if set to <c>true</c> [is live].</param>
- /// <returns>Task{System.Object}.</returns>
- /// <exception cref="ArgumentException">A video bitrate is required
- /// or
- /// An audio bitrate is required</exception>
- private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive)
- {
- var cancellationTokenSource = new CancellationTokenSource();
-
- var state = await GetState(request, cancellationTokenSource.Token).ConfigureAwait(false);
-
- TranscodingJob job = null;
- var playlist = state.OutputFilePath;
-
- if (!FileSystem.FileExists(playlist))
- {
- var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist);
- await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
- try
- {
- if (!FileSystem.FileExists(playlist))
- {
- // If the playlist doesn't already exist, startup ffmpeg
- try
- {
- job = await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
- job.IsLiveOutput = isLive;
- }
- catch
- {
- state.Dispose();
- throw;
- }
-
- var minSegments = state.MinSegments;
- if (minSegments > 0)
- {
- await WaitForMinimumSegmentCount(playlist, minSegments, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- }
- }
- finally
- {
- transcodingLock.Release();
- }
- }
-
- if (isLive)
- {
- job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
-
- if (job != null)
- {
- ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
- }
- return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
- }
-
- var audioBitrate = state.OutputAudioBitrate ?? 0;
- var videoBitrate = state.OutputVideoBitrate ?? 0;
-
- var baselineStreamBitrate = 64000;
-
- var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, baselineStreamBitrate);
-
- job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
-
- if (job != null)
- {
- ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
- }
-
- return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
- }
-
- private string GetLivePlaylistText(string path, int segmentLength)
- {
- using (var stream = FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite))
- {
- using (var reader = new StreamReader(stream))
- {
- var text = reader.ReadToEnd();
-
- text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
-
- var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
-
- text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
- //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
-
- return text;
- }
- }
- }
-
- private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, int baselineStreamBitrate)
- {
- var builder = new StringBuilder();
-
- builder.AppendLine("#EXTM3U");
-
- // Pad a little to satisfy the apple hls validator
- var paddedBitrate = Convert.ToInt32(bitrate * 1.15);
-
- // Main stream
- builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture));
- var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
- builder.AppendLine(playlistUrl);
-
- return builder.ToString();
- }
-
- protected virtual async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken)
- {
- Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
-
- while (!cancellationToken.IsCancellationRequested)
- {
- try
- {
- // Need to use FileShareMode.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))
- {
- var count = 0;
-
- while (!reader.EndOfStream)
- {
- var line = reader.ReadLine();
-
- if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
- {
- count++;
- if (count >= segmentCount)
- {
- Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
- return;
- }
- }
- }
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
- }
- }
- catch (IOException)
- {
- // May get an error if the file is locked
- }
-
- await Task.Delay(50, cancellationToken).ConfigureAwait(false);
- }
- }
-
- protected Stream GetPlaylistFileStream(string path)
- {
- var tmpPath = path + ".tmp";
- tmpPath = path;
-
- try
- {
- return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
- }
- catch (IOException)
- {
- return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
- }
- }
-
- protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
- {
- var itsOffsetMs = 0;
-
- var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
-
- var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false);
-
- var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
-
- // If isEncoding is true we're actually starting ffmpeg
- var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
-
- var baseUrlParam = string.Empty;
-
- if (state.Request is GetLiveHlsStream)
- {
- baseUrlParam = string.Format(" -hls_base_url \"{0}/\"",
- "hls/" + Path.GetFileNameWithoutExtension(outputPath));
- }
-
- var useGenericSegmenter = true;
- if (useGenericSegmenter)
- {
- var outputTsArg = Path.Combine(FileSystem.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
-
- var timeDeltaParam = String.Empty;
-
- var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
- if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
- {
- segmentFormat = "mpegts";
- }
-
- baseUrlParam = string.Format("\"{0}/\"", "hls/" + Path.GetFileNameWithoutExtension(outputPath));
-
- return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_entry_prefix {12} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
- inputModifier,
- EncodingHelper.GetInputArgument(state, encodingOptions),
- threads,
- EncodingHelper.GetMapArgs(state),
- GetVideoArguments(state, encodingOptions),
- GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
- startNumberParam,
- outputPath,
- outputTsArg,
- timeDeltaParam,
- segmentFormat,
- baseUrlParam
- ).Trim();
- }
-
- // add when stream copying?
- // -avoid_negative_ts make_zero -fflags +genpts
-
- var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero {6} -hls_time {7} -individual_header_trailer 0 -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
- itsOffset,
- inputModifier,
- EncodingHelper.GetInputArgument(state, encodingOptions),
- threads,
- EncodingHelper.GetMapArgs(state),
- GetVideoArguments(state, encodingOptions),
- GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
- startNumberParam,
- state.HlsListSize.ToString(UsCulture),
- baseUrlParam,
- outputPath
- ).Trim();
-
- return args;
- }
-
- protected override string GetDefaultH264Preset()
- {
- return "veryfast";
- }
-
- protected virtual int GetStartNumber(StreamState state)
- {
- return 0;
- }
-
- public BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
- {
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
deleted file mode 100644
index 6744fbd92..000000000
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ /dev/null
@@ -1,970 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-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;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Services;
-using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
-
-namespace MediaBrowser.Api.Playback.Hls
-{
- /// <summary>
- /// Options is needed for chromecast. Threw Head in there since it's related
- /// </summary>
- [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
- [Route("/Videos/{Id}/master.m3u8", "HEAD", Summary = "Gets a video stream using HTTP live streaming.")]
- public class GetMasterHlsVideoPlaylist : VideoStreamRequest, IMasterHlsRequest
- {
- public bool EnableAdaptiveBitrateStreaming { get; set; }
-
- public GetMasterHlsVideoPlaylist()
- {
- EnableAdaptiveBitrateStreaming = true;
- }
- }
-
- [Route("/Audio/{Id}/master.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
- [Route("/Audio/{Id}/master.m3u8", "HEAD", Summary = "Gets an audio stream using HTTP live streaming.")]
- public class GetMasterHlsAudioPlaylist : StreamRequest, IMasterHlsRequest
- {
- public bool EnableAdaptiveBitrateStreaming { get; set; }
-
- public GetMasterHlsAudioPlaylist()
- {
- EnableAdaptiveBitrateStreaming = true;
- }
- }
-
- public interface IMasterHlsRequest
- {
- bool EnableAdaptiveBitrateStreaming { get; set; }
- }
-
- [Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
- public class GetVariantHlsVideoPlaylist : VideoStreamRequest
- {
- }
-
- [Route("/Audio/{Id}/main.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
- public class GetVariantHlsAudioPlaylist : StreamRequest
- {
- }
-
- [Route("/Videos/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
- public class GetHlsVideoSegment : VideoStreamRequest
- {
- public string PlaylistId { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- [Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
- public class GetHlsAudioSegment : StreamRequest
- {
- public string PlaylistId { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- [Authenticated]
- public class DynamicHlsService : BaseHlsService
- {
-
- public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
- {
- NetworkManager = networkManager;
- }
-
- protected INetworkManager NetworkManager { get; private set; }
-
- public Task<object> Get(GetMasterHlsVideoPlaylist request)
- {
- return GetMasterPlaylistInternal(request, "GET");
- }
-
- public Task<object> Head(GetMasterHlsVideoPlaylist request)
- {
- return GetMasterPlaylistInternal(request, "HEAD");
- }
-
- public Task<object> Get(GetMasterHlsAudioPlaylist request)
- {
- return GetMasterPlaylistInternal(request, "GET");
- }
-
- public Task<object> Head(GetMasterHlsAudioPlaylist request)
- {
- return GetMasterPlaylistInternal(request, "HEAD");
- }
-
- public Task<object> Get(GetVariantHlsVideoPlaylist request)
- {
- return GetVariantPlaylistInternal(request, true, "main");
- }
-
- public Task<object> Get(GetVariantHlsAudioPlaylist request)
- {
- return GetVariantPlaylistInternal(request, false, "main");
- }
-
- public Task<object> Get(GetHlsVideoSegment request)
- {
- return GetDynamicSegment(request, request.SegmentId);
- }
-
- public Task<object> Get(GetHlsAudioSegment request)
- {
- return GetDynamicSegment(request, request.SegmentId);
- }
-
- private async Task<object> GetDynamicSegment(StreamRequest request, string segmentId)
- {
- if ((request.StartTimeTicks ?? 0) > 0)
- {
- throw new ArgumentException("StartTimeTicks is not allowed.");
- }
-
- var cancellationTokenSource = new CancellationTokenSource();
- var cancellationToken = cancellationTokenSource.Token;
-
- var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture);
-
- var state = await GetState(request, cancellationToken).ConfigureAwait(false);
-
- var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
-
- var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
-
- var segmentExtension = GetSegmentFileExtension(state.Request);
-
- TranscodingJob job = null;
-
- if (FileSystem.FileExists(segmentPath))
- {
- job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
- }
-
- var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath);
- await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
- var released = false;
- var startTranscoding = false;
-
- try
- {
- if (FileSystem.FileExists(segmentPath))
- {
- job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- transcodingLock.Release();
- released = true;
- return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
- var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
-
- if (currentTranscodingIndex == null)
- {
- Logger.Debug("Starting transcoding because currentTranscodingIndex=null");
- startTranscoding = true;
- }
- else if (requestedIndex < currentTranscodingIndex.Value)
- {
- Logger.Debug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", requestedIndex, currentTranscodingIndex);
- startTranscoding = true;
- }
- else if (requestedIndex - currentTranscodingIndex.Value > segmentGapRequiringTranscodingChange)
- {
- Logger.Debug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", requestedIndex - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, requestedIndex);
- startTranscoding = true;
- }
- if (startTranscoding)
- {
- // If the playlist doesn't already exist, startup ffmpeg
- try
- {
- ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false);
-
- if (currentTranscodingIndex.HasValue)
- {
- DeleteLastFile(playlistPath, segmentExtension, 0);
- }
-
- request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
-
- job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
- }
- catch
- {
- state.Dispose();
- throw;
- }
-
- //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- else
- {
- job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- if (job.TranscodingThrottler != null)
- {
- job.TranscodingThrottler.UnpauseTranscoding();
- }
- }
- }
- }
- finally
- {
- if (!released)
- {
- transcodingLock.Release();
- }
- }
-
- //Logger.Info("waiting for {0}", segmentPath);
- //while (!File.Exists(segmentPath))
- //{
- // await Task.Delay(50, cancellationToken).ConfigureAwait(false);
- //}
-
- Logger.Info("returning {0}", segmentPath);
- job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
- }
-
- private const int BufferSize = 81920;
-
- private long GetStartPositionTicks(StreamState state, int requestedIndex)
- {
- double startSeconds = 0;
- var lengths = GetSegmentLengths(state);
-
- if (requestedIndex >= lengths.Length)
- {
- var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length);
- throw new ArgumentException(msg);
- }
-
- for (var i = 0; i < requestedIndex; i++)
- {
- startSeconds += lengths[i];
- }
-
- var position = TimeSpan.FromSeconds(startSeconds).Ticks;
- return position;
- }
-
- private long GetEndPositionTicks(StreamState state, int requestedIndex)
- {
- double startSeconds = 0;
- var lengths = GetSegmentLengths(state);
-
- if (requestedIndex >= lengths.Length)
- {
- var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length);
- throw new ArgumentException(msg);
- }
-
- for (var i = 0; i <= requestedIndex; i++)
- {
- startSeconds += lengths[i];
- }
-
- var position = TimeSpan.FromSeconds(startSeconds).Ticks;
- return position;
- }
-
- private double[] GetSegmentLengths(StreamState state)
- {
- var result = new List<double>();
-
- var ticks = state.RunTimeTicks ?? 0;
-
- var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
-
- while (ticks > 0)
- {
- var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
-
- result.Add(TimeSpan.FromTicks(length).TotalSeconds);
-
- ticks -= length;
- }
-
- return result.ToArray();
- }
-
- public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
- {
- var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
-
- if (job == null || job.HasExited)
- {
- return null;
- }
-
- var file = GetLastTranscodingFile(playlist, segmentExtension, FileSystem);
-
- if (file == null)
- {
- return null;
- }
-
- var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
-
- var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length);
-
- return int.Parse(indexString, NumberStyles.Integer, UsCulture);
- }
-
- private void DeleteLastFile(string playlistPath, string segmentExtension, int retryCount)
- {
- var file = GetLastTranscodingFile(playlistPath, segmentExtension, FileSystem);
-
- if (file != null)
- {
- DeleteFile(file.FullName, retryCount);
- }
- }
-
- private void DeleteFile(string path, int retryCount)
- {
- if (retryCount >= 5)
- {
- return;
- }
-
- Logger.Debug("Deleting partial HLS file {0}", path);
-
- try
- {
- FileSystem.DeleteFile(path);
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
-
- var task = Task.Delay(100);
- Task.WaitAll(task);
- DeleteFile(path, retryCount + 1);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
- }
- }
-
- private static FileSystemMetadata GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem)
- {
- var folder = fileSystem.GetDirectoryName(playlist);
-
- var filePrefix = Path.GetFileNameWithoutExtension(playlist) ?? string.Empty;
-
- try
- {
- return fileSystem.GetFiles(folder, new[] { segmentExtension }, true, false)
- .Where(i => Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
- .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
- .FirstOrDefault();
- }
- catch (IOException)
- {
- return null;
- }
- }
-
- protected override int GetStartNumber(StreamState state)
- {
- return GetStartNumber(state.VideoRequest);
- }
-
- private int GetStartNumber(VideoStreamRequest request)
- {
- var segmentId = "0";
-
- var segmentRequest = request as GetHlsVideoSegment;
- if (segmentRequest != null)
- {
- segmentId = segmentRequest.SegmentId;
- }
-
- return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
- }
-
- private string GetSegmentPath(StreamState state, string playlist, int index)
- {
- var folder = FileSystem.GetDirectoryName(playlist);
-
- var filename = Path.GetFileNameWithoutExtension(playlist);
-
- return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state.Request));
- }
-
- private async Task<object> GetSegmentResult(StreamState state,
- string playlistPath,
- string segmentPath,
- string segmentExtension,
- int segmentIndex,
- TranscodingJob transcodingJob,
- CancellationToken cancellationToken)
- {
- var segmentFileExists = FileSystem.FileExists(segmentPath);
-
- // If all transcoding has completed, just return immediately
- if (transcodingJob != null && transcodingJob.HasExited && segmentFileExists)
- {
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
- }
-
- if (segmentFileExists)
- {
- var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
-
- // If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready
- if (segmentIndex < currentTranscodingIndex)
- {
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
- }
- }
-
- var segmentFilename = Path.GetFileName(segmentPath);
-
- while (!cancellationToken.IsCancellationRequested)
- {
- try
- {
- var text = FileSystem.ReadAllText(playlistPath, Encoding.UTF8);
-
- // If it appears in the playlist, it's done
- if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
- {
- if (!segmentFileExists)
- {
- segmentFileExists = FileSystem.FileExists(segmentPath);
- }
- if (segmentFileExists)
- {
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
- }
- //break;
- }
- }
- catch (IOException)
- {
- // May get an error if the file is locked
- }
-
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
- }
-
- private Task<object> GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
- {
- var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
-
- return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
- {
- Path = segmentPath,
- FileShare = FileShareMode.ReadWrite,
- OnComplete = () =>
- {
- if (transcodingJob != null)
- {
- transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
- ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
- }
- }
- });
- }
-
- private async Task<object> GetMasterPlaylistInternal(StreamRequest request, string method)
- {
- var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
-
- if (string.IsNullOrEmpty(request.MediaSourceId))
- {
- throw new ArgumentException("MediaSourceId is required");
- }
-
- var playlistText = string.Empty;
-
- if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
- {
- var audioBitrate = state.OutputAudioBitrate ?? 0;
- var videoBitrate = state.OutputVideoBitrate ?? 0;
-
- playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate);
- }
-
- return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
- }
-
- private string GetMasterPlaylistFileText(StreamState state, int totalBitrate)
- {
- var builder = new StringBuilder();
-
- builder.AppendLine("#EXTM3U");
-
- var isLiveStream = state.IsSegmentedLiveStream;
-
- var queryStringIndex = Request.RawUrl.IndexOf('?');
- var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
-
- // from universal audio service
- if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer))
- {
- queryString += "&SegmentContainer=" + state.Request.SegmentContainer;
- }
- // from universal audio service
- if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1)
- {
- queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons;
- }
-
- // Main stream
- var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8";
-
- playlistUrl += queryString;
-
- var request = state.Request;
-
- var subtitleStreams = state.MediaSource
- .MediaStreams
- .Where(i => i.IsTextSubtitleStream)
- .ToList();
-
- var subtitleGroup = subtitleStreams.Count > 0 &&
- request is GetMasterHlsVideoPlaylist &&
- (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Hls || state.VideoRequest.EnableSubtitlesInManifest) ?
- "subs" :
- null;
-
- // If we're burning in subtitles then don't add additional subs to the manifest
- if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
- {
- subtitleGroup = null;
- }
-
- if (!string.IsNullOrWhiteSpace(subtitleGroup))
- {
- AddSubtitles(state, subtitleStreams, builder);
- }
-
- AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
-
- if (EnableAdaptiveBitrateStreaming(state, isLiveStream))
- {
- var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0;
-
- // By default, vary by just 200k
- var variation = GetBitrateVariation(totalBitrate);
-
- var newBitrate = totalBitrate - variation;
- var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation);
- AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
-
- variation *= 2;
- newBitrate = totalBitrate - variation;
- variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation);
- AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
- }
-
- return builder.ToString();
- }
-
- private string ReplaceBitrate(string url, int oldValue, int newValue)
- {
- return url.Replace(
- "videobitrate=" + oldValue.ToString(UsCulture),
- "videobitrate=" + newValue.ToString(UsCulture),
- StringComparison.OrdinalIgnoreCase);
- }
-
- private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder)
- {
- var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index;
-
- foreach (var stream in subtitles)
- {
- const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\"";
-
- var name = stream.DisplayTitle;
-
- var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index;
- var isForced = stream.IsForced;
-
- var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}",
- state.Request.MediaSourceId,
- stream.Index.ToString(UsCulture),
- 30.ToString(UsCulture),
- AuthorizationContext.GetAuthorizationInfo(Request).Token);
-
- var line = string.Format(format,
- name,
- isDefault ? "YES" : "NO",
- isForced ? "YES" : "NO",
- url,
- stream.Language ?? "Unknown");
-
- builder.AppendLine(line);
- }
- }
-
- private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream)
- {
- // Within the local network this will likely do more harm than good.
- if (Request.IsLocal || NetworkManager.IsInLocalNetwork(Request.RemoteIp))
- {
- return false;
- }
-
- var request = state.Request as IMasterHlsRequest;
- if (request != null && !request.EnableAdaptiveBitrateStreaming)
- {
- return false;
- }
-
- if (isLiveStream || string.IsNullOrWhiteSpace(state.MediaPath))
- {
- // Opening live streams is so slow it's not even worth it
- return false;
- }
-
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (!state.IsOutputVideo)
- {
- return false;
- }
-
- // Having problems in android
- return false;
- //return state.VideoRequest.VideoBitRate.HasValue;
- }
-
- private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup)
- {
- var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(UsCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(UsCulture);
-
- // tvos wants resolution, codecs, framerate
- //if (state.TargetFramerate.HasValue)
- //{
- // header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture));
- //}
-
- if (!string.IsNullOrWhiteSpace(subtitleGroup))
- {
- header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup);
- }
-
- builder.AppendLine(header);
- builder.AppendLine(url);
- }
-
- private int GetBitrateVariation(int bitrate)
- {
- // By default, vary by just 50k
- var variation = 50000;
-
- if (bitrate >= 10000000)
- {
- variation = 2000000;
- }
- else if (bitrate >= 5000000)
- {
- variation = 1500000;
- }
- else if (bitrate >= 3000000)
- {
- variation = 1000000;
- }
- else if (bitrate >= 2000000)
- {
- variation = 500000;
- }
- else if (bitrate >= 1000000)
- {
- variation = 300000;
- }
- else if (bitrate >= 600000)
- {
- variation = 200000;
- }
- else if (bitrate >= 400000)
- {
- variation = 100000;
- }
-
- return variation;
- }
-
- private async Task<object> GetVariantPlaylistInternal(StreamRequest request, bool isOutputVideo, string name)
- {
- var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
-
- var segmentLengths = GetSegmentLengths(state);
-
- var builder = new StringBuilder();
-
- builder.AppendLine("#EXTM3U");
- builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
- builder.AppendLine("#EXT-X-VERSION:3");
- builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength).ToString(UsCulture));
- builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
-
- var queryStringIndex = Request.RawUrl.IndexOf('?');
- var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
-
- //if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1)
- //{
- // queryString = string.Empty;
- //}
-
- var index = 0;
-
- foreach (var length in segmentLengths)
- {
- builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ", nodesc");
-
- builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}",
-
- name,
- index.ToString(UsCulture),
- GetSegmentFileExtension(request),
- queryString));
-
- index++;
- }
-
- builder.AppendLine("#EXT-X-ENDLIST");
-
- var playlistText = builder.ToString();
-
- return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
- }
-
- protected override string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
- {
- var audioCodec = EncodingHelper.GetAudioEncoder(state);
-
- if (!state.IsOutputVideo)
- {
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return "-acodec copy";
- }
-
- var audioTranscodeParams = new List<string>();
-
- audioTranscodeParams.Add("-acodec " + audioCodec);
-
- if (state.OutputAudioBitrate.HasValue)
- {
- audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture));
- }
-
- if (state.OutputAudioChannels.HasValue)
- {
- audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
- }
-
- if (state.OutputAudioSampleRate.HasValue)
- {
- audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
- }
-
- audioTranscodeParams.Add("-vn");
- return string.Join(" ", audioTranscodeParams.ToArray());
- }
-
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
-
- if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.EnableBreakOnNonKeyFrames(videoCodec))
- {
- return "-codec:a:0 copy -copypriorss:a:0 0";
- }
-
- return "-codec:a:0 copy";
- }
-
- var args = "-codec:a:0 " + audioCodec;
-
- var channels = state.OutputAudioChannels;
-
- if (channels.HasValue)
- {
- args += " -ac " + channels.Value;
- }
-
- var bitrate = state.OutputAudioBitrate;
-
- if (bitrate.HasValue)
- {
- args += " -ab " + bitrate.Value.ToString(UsCulture);
- }
-
- if (state.OutputAudioSampleRate.HasValue)
- {
- args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
- }
-
- args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
-
- return args;
- }
-
- protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
- {
- if (!state.IsOutputVideo)
- {
- return string.Empty;
- }
-
- var codec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
-
- var args = "-codec:v:0 " + codec;
-
- if (state.EnableMpegtsM2TsMode)
- {
- args += " -mpegts_m2ts_mode 1";
- }
-
- // See if we can save come cpu cycles by avoiding encoding
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
- {
- args += " -bsf:v h264_mp4toannexb";
- }
-
- //args += " -flags -global_header";
- }
- else
- {
- var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
- state.SegmentLength.ToString(UsCulture));
-
- var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
-
- args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
-
- //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
-
- // Add resolution params, if specified
- if (!hasGraphicalSubs)
- {
- args += EncodingHelper.GetOutputSizeParam(state, codec, true);
- }
-
- // This is for internal graphical subs
- if (hasGraphicalSubs)
- {
- args += EncodingHelper.GetGraphicalSubtitleParam(state, codec);
- }
-
- //args += " -flags -global_header";
- }
-
- if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
- {
- args += " -copyts";
- }
-
- if (!string.IsNullOrEmpty(state.OutputVideoSync))
- {
- args += " -vsync " + state.OutputVideoSync;
- }
-
- args += EncodingHelper.GetOutputFFlags(state);
-
- return args;
- }
-
- protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
- {
- var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false);
-
- var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
-
- // If isEncoding is true we're actually starting ffmpeg
- var startNumber = GetStartNumber(state);
- var startNumberParam = isEncoding ? startNumber.ToString(UsCulture) : "0";
-
- var mapArgs = state.IsOutputVideo ? EncodingHelper.GetMapArgs(state) : string.Empty;
-
- var outputTsArg = Path.Combine(FileSystem.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
-
- var timeDeltaParam = String.Empty;
-
- if (isEncoding && startNumber > 0)
- {
- var startTime = state.SegmentLength * startNumber;
- timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime);
- }
-
- var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
- if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
- {
- segmentFormat = "mpegts";
- }
-
- var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
- var breakOnNonKeyFrames = state.EnableBreakOnNonKeyFrames(videoCodec);
-
- var breakOnNonKeyFramesArg = breakOnNonKeyFrames ? " -break_non_keyframes 1" : "";
-
- return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0{12} -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
- inputModifier,
- EncodingHelper.GetInputArgument(state, encodingOptions),
- threads,
- mapArgs,
- GetVideoArguments(state, encodingOptions),
- GetAudioArguments(state, encodingOptions),
- state.SegmentLength.ToString(UsCulture),
- startNumberParam,
- outputPath,
- outputTsArg,
- timeDeltaParam,
- segmentFormat,
- breakOnNonKeyFramesArg
- ).Trim();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
deleted file mode 100644
index 52cc02528..000000000
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Playback.Hls
-{
- /// <summary>
- /// Class GetHlsAudioSegment
- /// </summary>
- // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
- //[Authenticated]
- [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
- [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
- public class GetHlsAudioSegmentLegacy
- {
- // TODO: Deprecate with new iOS app
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- /// <summary>
- /// Class GetHlsVideoSegment
- /// </summary>
- [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
- [Authenticated]
- public class GetHlsPlaylistLegacy
- {
- // TODO: Deprecate with new iOS app
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- public string PlaylistId { get; set; }
- }
-
- [Route("/Videos/ActiveEncodings", "DELETE")]
- [Authenticated]
- public class StopEncodingProcess
- {
- [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string DeviceId { get; set; }
-
- [ApiMember(Name = "PlaySessionId", Description = "The play session id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string PlaySessionId { get; set; }
- }
-
- /// <summary>
- /// Class GetHlsVideoSegment
- /// </summary>
- // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
- //[Authenticated]
- [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
- public class GetHlsVideoSegmentLegacy : VideoStreamRequest
- {
- public string PlaylistId { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- public class HlsSegmentService : BaseApiService
- {
- private readonly IServerApplicationPaths _appPaths;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
-
- public HlsSegmentService(IServerApplicationPaths appPaths, IServerConfigurationManager config, IFileSystem fileSystem)
- {
- _appPaths = appPaths;
- _config = config;
- _fileSystem = fileSystem;
- }
-
- public Task<object> Get(GetHlsPlaylistLegacy request)
- {
- var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
- file = Path.Combine(_appPaths.TranscodingTempPath, file);
-
- return GetFileResult(file, file);
- }
-
- public void Delete(StopEncodingProcess request)
- {
- ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, path => true);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Get(GetHlsVideoSegmentLegacy request)
- {
- var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
-
- var transcodeFolderPath = _config.ApplicationPaths.TranscodingTempPath;
- file = Path.Combine(transcodeFolderPath, file);
-
- var normalizedPlaylistId = request.PlaylistId;
-
- var playlistPath = _fileSystem.GetFilePaths(transcodeFolderPath)
- .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
-
- return GetFileResult(file, playlistPath);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Get(GetHlsAudioSegmentLegacy request)
- {
- // TODO: Deprecate with new iOS app
- var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
- file = Path.Combine(_appPaths.TranscodingTempPath, file);
-
- return ResultFactory.GetStaticFileResult(Request, file, FileShareMode.ReadWrite);
- }
-
- private Task<object> GetFileResult(string path, string playlistPath)
- {
- var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
-
- return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
- {
- Path = path,
- FileShare = FileShareMode.ReadWrite,
- OnComplete = () =>
- {
- if (transcodingJob != null)
- {
- ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
- }
- }
- });
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
deleted file mode 100644
index 9b3c8a08f..000000000
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using System;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Playback.Hls
-{
- [Route("/Videos/{Id}/live.m3u8", "GET")]
- public class GetLiveHlsStream : VideoStreamRequest
- {
- }
-
- /// <summary>
- /// Class VideoHlsService
- /// </summary>
- [Authenticated]
- public class VideoHlsService : BaseHlsService
- {
- public object Get(GetLiveHlsStream request)
- {
- return ProcessRequest(request, true);
- }
-
- /// <summary>
- /// Gets the audio arguments.
- /// </summary>
- protected override string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
- {
- var codec = EncodingHelper.GetAudioEncoder(state);
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return "-codec:a:0 copy";
- }
-
- var args = "-codec:a:0 " + codec;
-
- var channels = state.OutputAudioChannels;
-
- if (channels.HasValue)
- {
- args += " -ac " + channels.Value;
- }
-
- var bitrate = state.OutputAudioBitrate;
-
- if (bitrate.HasValue)
- {
- args += " -ab " + bitrate.Value.ToString(UsCulture);
- }
-
- if (state.OutputAudioSampleRate.HasValue)
- {
- args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
- }
-
- args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
-
- return args;
- }
-
- /// <summary>
- /// Gets the video arguments.
- /// </summary>
- protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
- {
- if (!state.IsOutputVideo)
- {
- return string.Empty;
- }
-
- var codec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
-
- var args = "-codec:v:0 " + codec;
-
- if (state.EnableMpegtsM2TsMode)
- {
- args += " -mpegts_m2ts_mode 1";
- }
-
- // See if we can save come cpu cycles by avoiding encoding
- if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
- {
- // if h264_mp4toannexb is ever added, do not use it for live tv
- if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) &&
- !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
- {
- args += " -bsf:v h264_mp4toannexb";
- }
- }
- else
- {
- var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
- state.SegmentLength.ToString(UsCulture));
-
- var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
-
- args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
-
- // Add resolution params, if specified
- if (!hasGraphicalSubs)
- {
- args += EncodingHelper.GetOutputSizeParam(state, codec);
- }
-
- // This is for internal graphical subs
- if (hasGraphicalSubs)
- {
- args += EncodingHelper.GetGraphicalSubtitleParam(state, codec);
- }
- }
-
- args += " -flags -global_header";
-
- if (!string.IsNullOrEmpty(state.OutputVideoSync))
- {
- args += " -vsync " + state.OutputVideoSync;
- }
-
- args += EncodingHelper.GetOutputFFlags(state);
-
- return args;
- }
-
- public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
- {
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
deleted file mode 100644
index 536236f5f..000000000
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ /dev/null
@@ -1,591 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Playback
-{
- [Route("/Items/{Id}/PlaybackInfo", "GET", Summary = "Gets live playback media info for an item")]
- public class GetPlaybackInfo : IReturn<PlaybackInfoResponse>
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
- }
-
- [Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")]
- public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse>
- {
- }
-
- [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")]
- public class OpenMediaSource : LiveStreamRequest, IReturn<LiveStreamResponse>
- {
- }
-
- [Route("/LiveStreams/Close", "POST", Summary = "Closes a media source")]
- public class CloseMediaSource : IReturnVoid
- {
- [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string LiveStreamId { get; set; }
- }
-
- [Route("/Playback/BitrateTest", "GET")]
- public class GetBitrateTestBytes
- {
- [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
- public long Size { get; set; }
-
- public GetBitrateTestBytes()
- {
- // 100k
- Size = 102400;
- }
- }
-
- [Authenticated]
- public class MediaInfoService : BaseApiService
- {
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IDeviceManager _deviceManager;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly INetworkManager _networkManager;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly IUserManager _userManager;
- private readonly IJsonSerializer _json;
- private readonly IAuthorizationContext _authContext;
-
- public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json, IAuthorizationContext authContext)
- {
- _mediaSourceManager = mediaSourceManager;
- _deviceManager = deviceManager;
- _libraryManager = libraryManager;
- _config = config;
- _networkManager = networkManager;
- _mediaEncoder = mediaEncoder;
- _userManager = userManager;
- _json = json;
- _authContext = authContext;
- }
-
- public object Get(GetBitrateTestBytes request)
- {
- var bytes = new byte[request.Size];
-
- for (var i = 0; i < bytes.Length; i++)
- {
- bytes[i] = 0;
- }
-
- return ResultFactory.GetResult(bytes, "application/octet-stream");
- }
-
- public async Task<object> Get(GetPlaybackInfo request)
- {
- var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false);
- return ToOptimizedResult(result);
- }
-
- public async Task<object> Post(OpenMediaSource request)
- {
- var result = await OpenMediaSource(request).ConfigureAwait(false);
-
- return ToOptimizedResult(result);
- }
-
- private async Task<LiveStreamResponse> OpenMediaSource(OpenMediaSource request)
- {
- var authInfo = _authContext.GetAuthorizationInfo(Request);
-
- var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false);
-
- var profile = request.DeviceProfile;
- if (profile == null)
- {
- var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
- if (caps != null)
- {
- profile = caps.DeviceProfile;
- }
- }
-
- if (profile != null)
- {
- var item = _libraryManager.GetItemById(request.ItemId);
-
- SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
- request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
- request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, true, true, true);
- }
- else
- {
- if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl))
- {
- result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId;
- }
- }
-
- return result;
- }
-
- public void Post(CloseMediaSource request)
- {
- var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
- Task.WaitAll(task);
- }
-
- public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
- {
- var authInfo = _authContext.GetAuthorizationInfo(Request);
-
- var profile = request.DeviceProfile;
-
- //Logger.Info("GetPostedPlaybackInfo profile: {0}", _json.SerializeToString(profile));
-
- if (profile == null)
- {
- var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
- if (caps != null)
- {
- profile = caps.DeviceProfile;
- }
- }
-
- var info = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false);
-
- if (profile != null)
- {
- var mediaSourceId = request.MediaSourceId;
-
- SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, request.EnableTranscoding, request.AllowVideoStreamCopy, request.AllowAudioStreamCopy);
- }
-
- if (request.AutoOpenLiveStream)
- {
- var mediaSource = string.IsNullOrWhiteSpace(request.MediaSourceId) ? info.MediaSources.FirstOrDefault() : info.MediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId, StringComparison.Ordinal));
-
- if (mediaSource != null && mediaSource.RequiresOpening && string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
- {
- var openStreamResult = await OpenMediaSource(new OpenMediaSource
- {
- AudioStreamIndex = request.AudioStreamIndex,
- DeviceProfile = request.DeviceProfile,
- EnableDirectPlay = request.EnableDirectPlay,
- EnableDirectStream = request.EnableDirectStream,
- ItemId = request.Id,
- MaxAudioChannels = request.MaxAudioChannels,
- MaxStreamingBitrate = request.MaxStreamingBitrate,
- PlaySessionId = info.PlaySessionId,
- StartTimeTicks = request.StartTimeTicks,
- SubtitleStreamIndex = request.SubtitleStreamIndex,
- UserId = request.UserId,
- OpenToken = mediaSource.OpenToken,
- EnableMediaProbe = request.EnableMediaProbe
-
- }).ConfigureAwait(false);
-
- info.MediaSources = new List<MediaSourceInfo> { openStreamResult.MediaSource };
- }
- }
-
- return info;
- }
-
- public async Task<object> Post(GetPostedPlaybackInfo request)
- {
- var result = await GetPlaybackInfo(request).ConfigureAwait(false);
-
- 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(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null)
- {
- var result = new PlaybackInfoResponse();
-
- if (string.IsNullOrWhiteSpace(liveStreamId))
- {
- IEnumerable<MediaSourceInfo> mediaSources;
- try
- {
- mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, supportedLiveMediaTypes, CancellationToken.None).ConfigureAwait(false);
- }
- catch (PlaybackException ex)
- {
- mediaSources = new List<MediaSourceInfo>();
- result.ErrorCode = ex.ErrorCode;
- }
-
- result.MediaSources = mediaSources.ToList();
-
- if (!string.IsNullOrWhiteSpace(mediaSourceId))
- {
- result.MediaSources = result.MediaSources
- .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
- }
- else
- {
- var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
-
- result.MediaSources = new List<MediaSourceInfo> { mediaSource };
- }
-
- if (result.MediaSources.Count == 0)
- {
- if (!result.ErrorCode.HasValue)
- {
- result.ErrorCode = PlaybackErrorCode.NoCompatibleStream;
- }
- }
- else
- {
- result.MediaSources = Clone(result.MediaSources);
-
- result.PlaySessionId = Guid.NewGuid().ToString("N");
- }
-
- return result;
- }
-
- private void SetDeviceSpecificData(string itemId,
- PlaybackInfoResponse result,
- DeviceProfile profile,
- AuthorizationInfo auth,
- long? maxBitrate,
- long startTimeTicks,
- string mediaSourceId,
- int? audioStreamIndex,
- int? subtitleStreamIndex,
- int? maxAudioChannels,
- string userId,
- bool enableDirectPlay,
- bool forceDirectPlayRemoteMediaSource,
- bool enableDirectStream,
- bool enableTranscoding,
- bool allowVideoStreamCopy,
- bool allowAudioStreamCopy)
- {
- var item = _libraryManager.GetItemById(itemId);
-
- foreach (var mediaSource in result.MediaSources)
- {
- SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId, enableDirectPlay, forceDirectPlayRemoteMediaSource, enableDirectStream, enableTranscoding, allowVideoStreamCopy, allowAudioStreamCopy);
- }
-
- SortMediaSources(result, maxBitrate);
- }
-
- private void SetDeviceSpecificData(BaseItem item,
- MediaSourceInfo mediaSource,
- DeviceProfile profile,
- AuthorizationInfo auth,
- long? maxBitrate,
- long startTimeTicks,
- string mediaSourceId,
- int? audioStreamIndex,
- int? subtitleStreamIndex,
- int? maxAudioChannels,
- string playSessionId,
- string userId,
- bool enableDirectPlay,
- bool forceDirectPlayRemoteMediaSource,
- bool enableDirectStream,
- bool enableTranscoding,
- bool allowVideoStreamCopy,
- bool allowAudioStreamCopy)
- {
- var streamBuilder = new StreamBuilder(_mediaEncoder, Logger);
-
- var options = new VideoOptions
- {
- MediaSources = new List<MediaSourceInfo> { mediaSource },
- Context = EncodingContext.Streaming,
- DeviceId = auth.DeviceId,
- ItemId = item.Id.ToString("N"),
- Profile = profile,
- MaxAudioChannels = maxAudioChannels
- };
-
- if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
- {
- options.MediaSourceId = mediaSourceId;
- options.AudioStreamIndex = audioStreamIndex;
- options.SubtitleStreamIndex = subtitleStreamIndex;
- }
-
- var user = _userManager.GetUserById(userId);
-
- if (!enableDirectPlay)
- {
- mediaSource.SupportsDirectPlay = false;
- }
- if (!enableDirectStream)
- {
- mediaSource.SupportsDirectStream = false;
- }
- if (!enableTranscoding)
- {
- mediaSource.SupportsTranscoding = false;
- }
-
- if (item is Audio)
- {
- Logger.Info("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding);
- }
- else
- {
- Logger.Info("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
- user.Name,
- user.Policy.EnablePlaybackRemuxing,
- user.Policy.EnableVideoPlaybackTranscoding,
- user.Policy.EnableAudioPlaybackTranscoding);
- }
-
- if (mediaSource.SupportsDirectPlay)
- {
- if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource)
- {
- }
- else
- {
- var supportsDirectStream = mediaSource.SupportsDirectStream;
-
- // Dummy this up to fool StreamBuilder
- mediaSource.SupportsDirectStream = true;
- options.MaxBitrate = maxBitrate;
-
- if (item is Audio)
- {
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- options.ForceDirectPlay = true;
- }
- }
- else if (item is Video)
- {
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
- {
- options.ForceDirectPlay = 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);
-
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectPlay = false;
- }
-
- // Set this back to what it was
- mediaSource.SupportsDirectStream = supportsDirectStream;
-
- if (streamInfo != null)
- {
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- }
- }
- }
-
- if (mediaSource.SupportsDirectStream)
- {
- options.MaxBitrate = GetMaxBitrate(maxBitrate);
-
- if (item is Audio)
- {
- 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);
-
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectStream = false;
- }
-
- if (streamInfo != null)
- {
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- }
- }
-
- if (mediaSource.SupportsTranscoding)
- {
- options.MaxBitrate = GetMaxBitrate(maxBitrate);
-
- // 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);
-
- if (streamInfo != null)
- {
- streamInfo.PlaySessionId = playSessionId;
-
- if (streamInfo.PlayMethod == PlayMethod.Transcode)
- {
- 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;
- }
-
- // Do this after the above so that StartPositionTicks is set
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- }
- }
- }
-
- private long? GetMaxBitrate(long? clientMaxBitrate)
- {
- var maxBitrate = clientMaxBitrate;
- var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
-
- if (remoteClientMaxBitrate > 0)
- {
- var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp);
-
- Logger.Info("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork);
- if (!isInLocalNetwork)
- {
- maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
- }
- }
-
- return maxBitrate;
- }
-
- private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken)
- {
- var profiles = info.GetSubtitleProfiles(false, "-", accessToken);
- mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex;
-
- mediaSource.TranscodeReasons = info.TranscodeReasons;
-
- foreach (var profile in profiles)
- {
- foreach (var stream in mediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && stream.Index == profile.Index)
- {
- stream.DeliveryMethod = profile.DeliveryMethod;
-
- if (profile.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- stream.DeliveryUrl = profile.Url.TrimStart('-');
- stream.IsExternalUrl = profile.IsExternalUrl;
- }
- }
- }
- }
- }
-
- private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate)
- {
- var originalList = result.MediaSources.ToList();
-
- result.MediaSources = result.MediaSources.OrderBy(i =>
- {
- // Nothing beats direct playing a file
- if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File)
- {
- return 0;
- }
-
- return 1;
-
- }).ThenBy(i =>
- {
- // Let's assume direct streaming a file is just as desirable as direct playing a remote url
- if (i.SupportsDirectPlay || i.SupportsDirectStream)
- {
- return 0;
- }
-
- return 1;
-
- }).ThenBy(i =>
- {
- switch (i.Protocol)
- {
- case MediaProtocol.File:
- return 0;
- default:
- return 1;
- }
-
- }).ThenBy(i =>
- {
- if (maxBitrate.HasValue)
- {
- if (i.Bitrate.HasValue)
- {
- if (i.Bitrate.Value <= maxBitrate.Value)
- {
- return 0;
- }
-
- return 2;
- }
- }
-
- return 1;
-
- }).ThenBy(originalList.IndexOf)
- .ToList();
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
deleted file mode 100644
index 44e096dd7..000000000
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Api.Playback.Progressive
-{
- /// <summary>
- /// Class GetAudioStream
- /// </summary>
- [Route("/Audio/{Id}/stream.{Container}", "GET", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/stream.{Container}", "HEAD", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/stream", "HEAD", Summary = "Gets an audio stream")]
- public class GetAudioStream : StreamRequest
- {
- }
-
- /// <summary>
- /// Class AudioService
- /// </summary>
- // TODO: In order to autheneticate this in the future, Dlna playback will require updating
- //[Authenticated]
- public class AudioService : BaseProgressiveStreamingService
- {
- public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo)
- {
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Get(GetAudioStream request)
- {
- return ProcessRequest(request, false);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Head(GetAudioStream request)
- {
- return ProcessRequest(request, true);
- }
-
- protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
- {
- return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
deleted file mode 100644
index db5c78a2f..000000000
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ /dev/null
@@ -1,429 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Api.Playback.Progressive
-{
- /// <summary>
- /// Class BaseProgressiveStreamingService
- /// </summary>
- public abstract class BaseProgressiveStreamingService : BaseStreamingService
- {
- protected readonly IImageProcessor ImageProcessor;
- protected readonly IEnvironmentInfo EnvironmentInfo;
-
- public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
- {
- ImageProcessor = imageProcessor;
- EnvironmentInfo = environmentInfo;
- }
-
- /// <summary>
- /// Gets the output file extension.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected override string GetOutputFileExtension(StreamState state)
- {
- var ext = base.GetOutputFileExtension(state);
-
- if (!string.IsNullOrEmpty(ext))
- {
- return ext;
- }
-
- var isVideoRequest = state.VideoRequest != null;
-
- // Try to infer based on the desired video codec
- if (isVideoRequest)
- {
- var videoCodec = state.VideoRequest.VideoCodec;
-
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- return ".ts";
- }
- if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
- {
- return ".ogv";
- }
- if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
- {
- return ".webm";
- }
- if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
- {
- return ".asf";
- }
- }
-
- // Try to infer based on the desired audio codec
- if (!isVideoRequest)
- {
- var audioCodec = state.Request.AudioCodec;
-
- if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".aac";
- }
- if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".mp3";
- }
- if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".ogg";
- }
- if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".wma";
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the type of the transcoding job.
- /// </summary>
- /// <value>The type of the transcoding job.</value>
- protected override TranscodingJobType TranscodingJobType
- {
- get { return TranscodingJobType.Progressive; }
- }
-
- /// <summary>
- /// Processes the request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
- /// <returns>Task.</returns>
- protected async Task<object> ProcessRequest(StreamRequest request, bool isHeadRequest)
- {
- var cancellationTokenSource = new CancellationTokenSource();
-
- var state = await GetState(request, cancellationTokenSource.Token).ConfigureAwait(false);
-
- var responseHeaders = new Dictionary<string, string>();
-
- if (request.Static && state.DirectStreamProvider != null)
- {
- AddDlnaHeaders(state, responseHeaders, true);
-
- using (state)
- {
- var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- // TODO: Don't hardcode this
- outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
-
- return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None)
- {
- AllowEndOfFile = false
- };
- }
- }
-
- // Static remote stream
- if (request.Static && state.InputProtocol == MediaProtocol.Http)
- {
- AddDlnaHeaders(state, responseHeaders, true);
-
- using (state)
- {
- return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
- }
- }
-
- if (request.Static && state.InputProtocol != MediaProtocol.File)
- {
- throw new ArgumentException(string.Format("Input protocol {0} cannot be streamed statically.", state.InputProtocol));
- }
-
- var outputPath = state.OutputFilePath;
- var outputPathExists = FileSystem.FileExists(outputPath);
-
- var transcodingJob = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
- var isTranscodeCached = outputPathExists && transcodingJob != null;
-
- AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);
-
- // Static stream
- if (request.Static)
- {
- var contentType = state.GetMimeType(state.MediaPath);
-
- using (state)
- {
- if (state.MediaSource.IsInfiniteStream)
- {
- var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- outputHeaders["Content-Type"] = contentType;
-
- return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None)
- {
- AllowEndOfFile = false
- };
- }
-
- TimeSpan? cacheDuration = null;
-
- if (!string.IsNullOrEmpty(request.Tag))
- {
- cacheDuration = TimeSpan.FromDays(365);
- }
-
- return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
- {
- ResponseHeaders = responseHeaders,
- ContentType = contentType,
- IsHeadRequest = isHeadRequest,
- Path = state.MediaPath,
- CacheDuration = cacheDuration
-
- }).ConfigureAwait(false);
- }
- }
-
- //// Not static but transcode cache file exists
- //if (isTranscodeCached && state.VideoRequest == null)
- //{
- // var contentType = state.GetMimeType(outputPath);
-
- // try
- // {
- // if (transcodingJob != null)
- // {
- // ApiEntryPoint.Instance.OnTranscodeBeginRequest(transcodingJob);
- // }
-
- // return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
- // {
- // ResponseHeaders = responseHeaders,
- // ContentType = contentType,
- // IsHeadRequest = isHeadRequest,
- // Path = outputPath,
- // FileShare = FileShareMode.ReadWrite,
- // OnComplete = () =>
- // {
- // if (transcodingJob != null)
- // {
- // ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
- // }
- // }
-
- // }).ConfigureAwait(false);
- // }
- // finally
- // {
- // state.Dispose();
- // }
- //}
-
- // Need to start ffmpeg
- try
- {
- return await GetStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
- }
- catch
- {
- state.Dispose();
-
- throw;
- }
- }
-
- /// <summary>
- /// Gets the static remote stream result.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
- /// <param name="cancellationTokenSource">The cancellation token source.</param>
- /// <returns>Task{System.Object}.</returns>
- private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
- {
- string useragent = null;
- state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
-
- var trySupportSeek = false;
-
- var options = new HttpRequestOptions
- {
- Url = state.MediaPath,
- UserAgent = useragent,
- BufferContent = false,
- CancellationToken = cancellationTokenSource.Token
- };
-
- if (trySupportSeek)
- {
- if (!string.IsNullOrWhiteSpace(Request.QueryString["Range"]))
- {
- options.RequestHeaders["Range"] = Request.QueryString["Range"];
- }
- }
- var response = await HttpClient.GetResponse(options).ConfigureAwait(false);
-
- if (trySupportSeek)
- {
- foreach (var name in new[] { "Content-Range", "Accept-Ranges" })
- {
- var val = response.Headers[name];
- if (!string.IsNullOrWhiteSpace(val))
- {
- responseHeaders[name] = val;
- }
- }
- }
- else
- {
- responseHeaders["Accept-Ranges"] = "none";
- }
-
- // Seeing cases of -1 here
- if (response.ContentLength.HasValue && response.ContentLength.Value >= 0)
- {
- responseHeaders["Content-Length"] = response.ContentLength.Value.ToString(UsCulture);
- }
-
- if (isHeadRequest)
- {
- using (response)
- {
- return ResultFactory.GetResult(new byte[] { }, response.ContentType, responseHeaders);
- }
- }
-
- var result = new StaticRemoteStreamWriter(response);
-
- result.Headers["Content-Type"] = response.ContentType;
-
- // Add the response headers to the result object
- foreach (var header in responseHeaders)
- {
- result.Headers[header.Key] = header.Value;
- }
-
- return result;
- }
-
- /// <summary>
- /// Gets the stream result.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
- /// <param name="cancellationTokenSource">The cancellation token source.</param>
- /// <returns>Task{System.Object}.</returns>
- private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
- {
- // Use the command line args with a dummy playlist path
- var outputPath = state.OutputFilePath;
-
- responseHeaders["Accept-Ranges"] = "none";
-
- var contentType = state.GetMimeType(outputPath);
-
- // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response
- // What we really want to do is hunt that down and remove that
- var contentLength = state.EstimateContentLength || isHeadRequest ? GetEstimatedContentLength(state) : null;
-
- if (contentLength.HasValue)
- {
- responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture);
- }
-
- // Headers only
- if (isHeadRequest)
- {
- var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders);
-
- var hasHeaders = streamResult as IHasHeaders;
- if (hasHeaders != null)
- {
- if (contentLength.HasValue)
- {
- hasHeaders.Headers["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- if (hasHeaders.Headers.ContainsKey("Content-Length"))
- {
- hasHeaders.Headers.Remove("Content-Length");
- }
- }
- }
-
- return streamResult;
- }
-
- var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
- await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
- try
- {
- TranscodingJob job;
-
- if (!FileSystem.FileExists(outputPath))
- {
- job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
- }
- else
- {
- job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
- state.Dispose();
- }
-
- var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- outputHeaders["Content-Type"] = contentType;
-
- // Add the response headers to the result object
- foreach (var item in responseHeaders)
- {
- outputHeaders[item.Key] = item.Value;
- }
-
- return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, EnvironmentInfo, CancellationToken.None);
- }
- finally
- {
- transcodingLock.Release();
- }
- }
-
- /// <summary>
- /// Gets the length of the estimated content.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.Nullable{System.Int64}.</returns>
- private long? GetEstimatedContentLength(StreamState state)
- {
- var totalBitrate = state.TotalOutputBitrate ?? 0;
-
- if (totalBitrate > 0 && state.RunTimeTicks.HasValue)
- {
- return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds / 8);
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
deleted file mode 100644
index a41b4cbf5..000000000
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Api.Playback.Progressive
-{
- /// <summary>
- /// Class GetVideoStream
- /// </summary>
- [Route("/Videos/{Id}/stream.mpegts", "GET")]
- [Route("/Videos/{Id}/stream.ts", "GET")]
- [Route("/Videos/{Id}/stream.webm", "GET")]
- [Route("/Videos/{Id}/stream.asf", "GET")]
- [Route("/Videos/{Id}/stream.wmv", "GET")]
- [Route("/Videos/{Id}/stream.ogv", "GET")]
- [Route("/Videos/{Id}/stream.mp4", "GET")]
- [Route("/Videos/{Id}/stream.m4v", "GET")]
- [Route("/Videos/{Id}/stream.mkv", "GET")]
- [Route("/Videos/{Id}/stream.mpeg", "GET")]
- [Route("/Videos/{Id}/stream.mpg", "GET")]
- [Route("/Videos/{Id}/stream.avi", "GET")]
- [Route("/Videos/{Id}/stream.m2ts", "GET")]
- [Route("/Videos/{Id}/stream.3gp", "GET")]
- [Route("/Videos/{Id}/stream.wmv", "GET")]
- [Route("/Videos/{Id}/stream.wtv", "GET")]
- [Route("/Videos/{Id}/stream.mov", "GET")]
- [Route("/Videos/{Id}/stream.iso", "GET")]
- [Route("/Videos/{Id}/stream.flv", "GET")]
- [Route("/Videos/{Id}/stream", "GET")]
- [Route("/Videos/{Id}/stream.ts", "HEAD")]
- [Route("/Videos/{Id}/stream.webm", "HEAD")]
- [Route("/Videos/{Id}/stream.asf", "HEAD")]
- [Route("/Videos/{Id}/stream.wmv", "HEAD")]
- [Route("/Videos/{Id}/stream.ogv", "HEAD")]
- [Route("/Videos/{Id}/stream.mp4", "HEAD")]
- [Route("/Videos/{Id}/stream.m4v", "HEAD")]
- [Route("/Videos/{Id}/stream.mkv", "HEAD")]
- [Route("/Videos/{Id}/stream.mpeg", "HEAD")]
- [Route("/Videos/{Id}/stream.mpg", "HEAD")]
- [Route("/Videos/{Id}/stream.avi", "HEAD")]
- [Route("/Videos/{Id}/stream.3gp", "HEAD")]
- [Route("/Videos/{Id}/stream.wmv", "HEAD")]
- [Route("/Videos/{Id}/stream.wtv", "HEAD")]
- [Route("/Videos/{Id}/stream.m2ts", "HEAD")]
- [Route("/Videos/{Id}/stream.mov", "HEAD")]
- [Route("/Videos/{Id}/stream.iso", "HEAD")]
- [Route("/Videos/{Id}/stream.flv", "HEAD")]
- [Route("/Videos/{Id}/stream", "HEAD")]
- public class GetVideoStream : VideoStreamRequest
- {
-
- }
-
- /// <summary>
- /// Class VideoService
- /// </summary>
- // TODO: In order to autheneticate this in the future, Dlna playback will require updating
- //[Authenticated]
- public class VideoService : BaseProgressiveStreamingService
- {
- public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo)
- {
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Get(GetVideoStream request)
- {
- return ProcessRequest(request, false);
- }
-
- /// <summary>
- /// Heads the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public Task<object> Head(GetVideoStream request)
- {
- return ProcessRequest(request, true);
- }
-
- protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
- {
- return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
deleted file mode 100644
index 6bb3b6b80..000000000
--- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Common.Net;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Playback
-{
- /// <summary>
- /// Class StaticRemoteStreamWriter
- /// </summary>
- public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders
- {
- /// <summary>
- /// The _input stream
- /// </summary>
- private readonly HttpResponseInfo _response;
-
- /// <summary>
- /// The _options
- /// </summary>
- private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
-
- public StaticRemoteStreamWriter(HttpResponseInfo response)
- {
- _response = response;
- }
-
- /// <summary>
- /// Gets the options.
- /// </summary>
- /// <value>The options.</value>
- public IDictionary<string, string> Headers
- {
- get { return _options; }
- }
-
- public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
- {
- using (_response)
- {
- await _response.Content.CopyToAsync(responseStream, 81920, cancellationToken).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
deleted file mode 100644
index 176b68f37..000000000
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Playback
-{
- /// <summary>
- /// Class StreamRequest
- /// </summary>
- public class StreamRequest : BaseEncodingJobOptions
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string MediaSourceId { get; set; }
-
- [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceId { get; set; }
-
- [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Container { get; set; }
-
- /// <summary>
- /// Gets or sets the audio codec.
- /// </summary>
- /// <value>The audio codec.</value>
- [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string AudioCodec { get; set; }
-
- [ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceProfileId { get; set; }
-
- public string Params { get; set; }
- public string PlaySessionId { get; set; }
- public string Tag { get; set; }
- public string SegmentContainer { get; set; }
-
- public int? SegmentLength { get; set; }
- public int? MinSegments { get; set; }
- }
-
- public class VideoStreamRequest : StreamRequest
- {
- /// <summary>
- /// Gets a value indicating whether this instance has fixed resolution.
- /// </summary>
- /// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
- public bool HasFixedResolution
- {
- get
- {
- return Width.HasValue || Height.HasValue;
- }
- }
-
- public bool EnableSubtitlesInManifest { get; set; }
- }
-}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
deleted file mode 100644
index eecc12432..000000000
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ /dev/null
@@ -1,259 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Net;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Controller.MediaEncoding;
-
-namespace MediaBrowser.Api.Playback
-{
- public class StreamState : EncodingJobInfo, IDisposable
- {
- private readonly ILogger _logger;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public string RequestedUrl { get; set; }
-
- public StreamRequest Request
- {
- get { return (StreamRequest)BaseRequest; }
- set
- {
- BaseRequest = value;
-
- IsVideoRequest = VideoRequest != null;
- }
- }
-
- public TranscodingThrottler TranscodingThrottler { get; set; }
-
- public VideoStreamRequest VideoRequest
- {
- get { return Request as VideoStreamRequest; }
- }
-
- /// <summary>
- /// Gets or sets the log file stream.
- /// </summary>
- /// <value>The log file stream.</value>
- public Stream LogFileStream { get; set; }
- public IDirectStreamProvider DirectStreamProvider { get; set; }
-
- public string WaitForPath { get; set; }
-
- public bool IsOutputVideo
- {
- get { return Request is VideoStreamRequest; }
- }
-
- public int SegmentLength
- {
- get
- {
- if (Request.SegmentLength.HasValue)
- {
- return Request.SegmentLength.Value;
- }
-
- if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var userAgent = UserAgent ?? string.Empty;
-
- if (userAgent.IndexOf("AppleTV", StringComparison.OrdinalIgnoreCase) != -1 ||
- userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) != -1 ||
- userAgent.IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
- userAgent.IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
- userAgent.IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
- {
- if (IsSegmentedLiveStream)
- {
- return 6;
- }
-
- return 6;
- }
-
- if (IsSegmentedLiveStream)
- {
- return 3;
- }
- return 6;
- }
-
- return 3;
- }
- }
-
- public int MinSegments
- {
- get
- {
- if (Request.MinSegments.HasValue)
- {
- return Request.MinSegments.Value;
- }
-
- return SegmentLength >= 10 ? 2 : 3;
- }
- }
-
- public int HlsListSize
- {
- get
- {
- return 0;
- }
- }
-
- public string UserAgent { get; set; }
-
- public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
- : base(logger, transcodingType)
- {
- _mediaSourceManager = mediaSourceManager;
- _logger = logger;
- }
-
- public string MimeType { get; set; }
-
- public bool EstimateContentLength { get; set; }
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-
- public long? EncodingDurationTicks { get; set; }
-
- public string GetMimeType(string outputPath)
- {
- if (!string.IsNullOrEmpty(MimeType))
- {
- return MimeType;
- }
-
- return MimeTypes.GetMimeType(outputPath);
- }
-
- public bool EnableDlnaHeaders { get; set; }
-
- public void Dispose()
- {
- DisposeTranscodingThrottler();
- DisposeLiveStream();
- DisposeLogStream();
- DisposeIsoMount();
-
- TranscodingJob = null;
- }
-
- private void DisposeLogStream()
- {
- if (LogFileStream != null)
- {
- try
- {
- LogFileStream.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing log stream", ex);
- }
-
- LogFileStream = null;
- }
- }
-
- private void DisposeTranscodingThrottler()
- {
- if (TranscodingThrottler != null)
- {
- try
- {
- TranscodingThrottler.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing TranscodingThrottler", ex);
- }
-
- TranscodingThrottler = null;
- }
- }
-
- private async void DisposeLiveStream()
- {
- if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId) && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing media source", ex);
- }
- }
- }
-
- public string OutputFilePath { get; set; }
-
- public string ActualOutputVideoCodec
- {
- get
- {
- var codec = OutputVideoCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = VideoStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
- public string ActualOutputAudioCodec
- {
- get
- {
- var codec = OutputAudioCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = AudioStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public TranscodingJob TranscodingJob;
- 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/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs
deleted file mode 100644
index c42d0c3e4..000000000
--- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Logging;
-using System;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Threading;
-
-namespace MediaBrowser.Api.Playback
-{
- public class TranscodingThrottler : IDisposable
- {
- private readonly TranscodingJob _job;
- private readonly ILogger _logger;
- private ITimer _timer;
- private bool _isPaused;
- private readonly IConfigurationManager _config;
- private readonly ITimerFactory _timerFactory;
- private readonly IFileSystem _fileSystem;
-
- public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config, ITimerFactory timerFactory, IFileSystem fileSystem)
- {
- _job = job;
- _logger = logger;
- _config = config;
- _timerFactory = timerFactory;
- _fileSystem = fileSystem;
- }
-
- private EncodingOptions GetOptions()
- {
- return _config.GetConfiguration<EncodingOptions>("encoding");
- }
-
- public void Start()
- {
- _timer = _timerFactory.Create(TimerCallback, null, 5000, 5000);
- }
-
- private void TimerCallback(object state)
- {
- if (_job.HasExited)
- {
- DisposeTimer();
- return;
- }
-
- var options = GetOptions();
-
- if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleDelaySeconds))
- {
- PauseTranscoding();
- }
- else
- {
- UnpauseTranscoding();
- }
- }
-
- private void PauseTranscoding()
- {
- if (!_isPaused)
- {
- _logger.Debug("Sending pause command to ffmpeg");
-
- try
- {
- _job.Process.StandardInput.Write("c");
- _isPaused = true;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error pausing transcoding", ex);
- }
- }
- }
-
- public void UnpauseTranscoding()
- {
- if (_isPaused)
- {
- _logger.Debug("Sending unpause command to ffmpeg");
-
- try
- {
- _job.Process.StandardInput.WriteLine();
- _isPaused = false;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error unpausing transcoding", ex);
- }
- }
- }
-
- private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds)
- {
- var bytesDownloaded = job.BytesDownloaded ?? 0;
- var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
- var downloadPositionTicks = job.DownloadPositionTicks ?? 0;
-
- var path = job.Path;
- var gapLengthInTicks = TimeSpan.FromSeconds(thresholdSeconds).Ticks;
-
- if (downloadPositionTicks > 0 && transcodingPositionTicks > 0)
- {
- // HLS - time-based consideration
-
- var targetGap = gapLengthInTicks;
- var gap = transcodingPositionTicks - downloadPositionTicks;
-
- if (gap < targetGap)
- {
- //_logger.Debug("Not throttling transcoder gap {0} target gap {1}", gap, targetGap);
- return false;
- }
-
- //_logger.Debug("Throttling transcoder gap {0} target gap {1}", gap, targetGap);
- return true;
- }
-
- if (bytesDownloaded > 0 && transcodingPositionTicks > 0)
- {
- // Progressive Streaming - byte-based consideration
-
- try
- {
- var bytesTranscoded = job.BytesTranscoded ?? _fileSystem.GetFileInfo(path).Length;
-
- // Estimate the bytes the transcoder should be ahead
- double gapFactor = gapLengthInTicks;
- gapFactor /= transcodingPositionTicks;
- var targetGap = bytesTranscoded * gapFactor;
-
- var gap = bytesTranscoded - bytesDownloaded;
-
- if (gap < targetGap)
- {
- //_logger.Debug("Not throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded);
- return false;
- }
-
- //_logger.Debug("Throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded);
- return true;
- }
- catch
- {
- //_logger.Error("Error getting output size");
- return false;
- }
- }
-
- //_logger.Debug("No throttle data for " + path);
- return false;
- }
-
- public void Stop()
- {
- DisposeTimer();
- UnpauseTranscoding();
- }
-
- public void Dispose()
- {
- DisposeTimer();
- }
-
- private void DisposeTimer()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
deleted file mode 100644
index 118bf5246..000000000
--- a/MediaBrowser.Api/Playback/UniversalAudioService.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Api.Playback.Hls;
-using MediaBrowser.Api.Playback.Progressive;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Api.Playback
-{
- public class BaseUniversalRequest
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string MediaSourceId { get; set; }
-
- [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceId { get; set; }
-
- public string UserId { get; set; }
- public string AudioCodec { get; set; }
- public string Container { get; set; }
-
- public int? MaxAudioChannels { get; set; }
- public int? TranscodingAudioChannels { get; set; }
-
- public long? MaxStreamingBitrate { get; set; }
-
- [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public long? StartTimeTicks { get; set; }
-
- public string TranscodingContainer { get; set; }
- public string TranscodingProtocol { get; set; }
- public int? MaxAudioSampleRate { get; set; }
- public int? MaxAudioBitDepth { get; set; }
-
- public bool EnableRedirection { get; set; }
- public bool EnableRemoteMedia { get; set; }
- public bool BreakOnNonKeyFrames { get; set; }
-
- public BaseUniversalRequest()
- {
- EnableRedirection = true;
- }
- }
-
- [Route("/Audio/{Id}/universal.{Container}", "GET", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/universal", "GET", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/universal.{Container}", "HEAD", Summary = "Gets an audio stream")]
- [Route("/Audio/{Id}/universal", "HEAD", Summary = "Gets an audio stream")]
- public class GetUniversalAudioStream : BaseUniversalRequest
- {
- }
-
- [Authenticated]
- public class UniversalAudioService : BaseApiService
- {
- public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager, IEnvironmentInfo environmentInfo)
- {
- ServerConfigurationManager = serverConfigurationManager;
- UserManager = userManager;
- LibraryManager = libraryManager;
- IsoManager = isoManager;
- MediaEncoder = mediaEncoder;
- FileSystem = fileSystem;
- DlnaManager = dlnaManager;
- DeviceManager = deviceManager;
- SubtitleEncoder = subtitleEncoder;
- MediaSourceManager = mediaSourceManager;
- ZipClient = zipClient;
- JsonSerializer = jsonSerializer;
- AuthorizationContext = authorizationContext;
- ImageProcessor = imageProcessor;
- NetworkManager = networkManager;
- EnvironmentInfo = environmentInfo;
- }
-
- protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
- protected IUserManager UserManager { get; private set; }
- protected ILibraryManager LibraryManager { get; private set; }
- protected IIsoManager IsoManager { get; private set; }
- protected IMediaEncoder MediaEncoder { get; private set; }
- protected IFileSystem FileSystem { get; private set; }
- protected IDlnaManager DlnaManager { get; private set; }
- protected IDeviceManager DeviceManager { get; private set; }
- protected ISubtitleEncoder SubtitleEncoder { get; private set; }
- protected IMediaSourceManager MediaSourceManager { get; private set; }
- protected IZipClient ZipClient { get; private set; }
- protected IJsonSerializer JsonSerializer { get; private set; }
- protected IAuthorizationContext AuthorizationContext { get; private set; }
- protected IImageProcessor ImageProcessor { get; private set; }
- protected INetworkManager NetworkManager { get; private set; }
- protected IEnvironmentInfo EnvironmentInfo { get; private set; }
-
- public Task<object> Get(GetUniversalAudioStream request)
- {
- return GetUniversalStream(request, false);
- }
-
- public Task<object> Head(GetUniversalAudioStream request)
- {
- return GetUniversalStream(request, true);
- }
-
- private DeviceProfile GetDeviceProfile(GetUniversalAudioStream request)
- {
- var deviceProfile = new DeviceProfile();
-
- var directPlayProfiles = new List<DirectPlayProfile>();
-
- directPlayProfiles.Add(new DirectPlayProfile
- {
- Type = DlnaProfileType.Audio,
- Container = request.Container
- });
-
- deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
-
- deviceProfile.TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Type = DlnaProfileType.Audio,
- Context = EncodingContext.Streaming,
- Container = request.TranscodingContainer,
- AudioCodec = request.AudioCodec,
- Protocol = request.TranscodingProtocol,
- BreakOnNonKeyFrames = request.BreakOnNonKeyFrames,
- MaxAudioChannels = request.TranscodingAudioChannels.HasValue ? request.TranscodingAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : null
- }
- };
-
- var codecProfiles = new List<CodecProfile>();
- var conditions = new List<ProfileCondition>();
-
- if (request.MaxAudioSampleRate.HasValue)
- {
- // codec profile
- conditions.Add(new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- IsRequired = false,
- Property = ProfileConditionValue.AudioSampleRate,
- Value = request.MaxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)
- });
- }
-
- if (request.MaxAudioBitDepth.HasValue)
- {
- // codec profile
- conditions.Add(new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- IsRequired = false,
- Property = ProfileConditionValue.AudioBitDepth,
- Value = request.MaxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture)
- });
- }
-
- if (request.MaxAudioChannels.HasValue)
- {
- // codec profile
- conditions.Add(new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- IsRequired = false,
- Property = ProfileConditionValue.AudioChannels,
- Value = request.MaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture)
- });
- }
-
- if (conditions.Count > 0)
- {
- // codec profile
- codecProfiles.Add(new CodecProfile
- {
- Type = CodecType.Audio,
- Container = request.Container,
- Conditions = conditions.ToArray()
- });
- }
-
- deviceProfile.CodecProfiles = codecProfiles.ToArray();
-
- return deviceProfile;
- }
-
- private async Task<object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
- {
- var deviceProfile = GetDeviceProfile(request);
-
- AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
-
- var mediaInfoService = new MediaInfoService(MediaSourceManager, DeviceManager, LibraryManager, ServerConfigurationManager, NetworkManager, MediaEncoder, UserManager, JsonSerializer, AuthorizationContext)
- {
- Request = Request
- };
-
- var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
- {
- Id = request.Id,
- MaxAudioChannels = request.MaxAudioChannels,
- MaxStreamingBitrate = request.MaxStreamingBitrate,
- StartTimeTicks = request.StartTimeTicks,
- UserId = request.UserId,
- DeviceProfile = deviceProfile,
- MediaSourceId = request.MediaSourceId
-
- }).ConfigureAwait(false);
-
- var mediaSource = playbackInfoResult.MediaSources[0];
-
- if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
- {
- if (request.EnableRedirection)
- {
- if (mediaSource.IsRemote && request.EnableRemoteMedia)
- {
- return ResultFactory.GetRedirectResult(mediaSource.Path);
- }
- }
- }
-
- var isStatic = mediaSource.SupportsDirectStream;
-
- if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
- {
- var service = new DynamicHlsService(ServerConfigurationManager,
- UserManager,
- LibraryManager,
- IsoManager,
- MediaEncoder,
- FileSystem,
- DlnaManager,
- SubtitleEncoder,
- DeviceManager,
- MediaSourceManager,
- ZipClient,
- JsonSerializer,
- AuthorizationContext,
- NetworkManager)
- {
- Request = Request
- };
-
- var transcodingProfile = deviceProfile.TranscodingProfiles[0];
-
- var newRequest = new GetMasterHlsAudioPlaylist
- {
- AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
- AudioCodec = transcodingProfile.AudioCodec,
- Container = ".m3u8",
- DeviceId = request.DeviceId,
- Id = request.Id,
- MaxAudioChannels = request.MaxAudioChannels,
- MediaSourceId = mediaSource.Id,
- PlaySessionId = playbackInfoResult.PlaySessionId,
- StartTimeTicks = request.StartTimeTicks,
- Static = isStatic,
- SegmentContainer = request.TranscodingContainer,
- AudioSampleRate = request.MaxAudioSampleRate,
- MaxAudioBitDepth = request.MaxAudioBitDepth,
- BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
- TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
- };
-
- if (isHeadRequest)
- {
- return await service.Head(newRequest).ConfigureAwait(false);
- }
- return await service.Get(newRequest).ConfigureAwait(false);
- }
- else
- {
- var service = new AudioService(ServerConfigurationManager,
- UserManager,
- LibraryManager,
- IsoManager,
- MediaEncoder,
- FileSystem,
- DlnaManager,
- SubtitleEncoder,
- DeviceManager,
- MediaSourceManager,
- ZipClient,
- JsonSerializer,
- AuthorizationContext,
- ImageProcessor,
- EnvironmentInfo)
- {
- Request = Request
- };
-
- var newRequest = new GetAudioStream
- {
- AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
- AudioCodec = request.AudioCodec,
- Container = isStatic ? null : ("." + mediaSource.TranscodingContainer),
- DeviceId = request.DeviceId,
- Id = request.Id,
- MaxAudioChannels = request.MaxAudioChannels,
- MediaSourceId = mediaSource.Id,
- PlaySessionId = playbackInfoResult.PlaySessionId,
- StartTimeTicks = request.StartTimeTicks,
- Static = isStatic,
- AudioSampleRate = request.MaxAudioSampleRate,
- MaxAudioBitDepth = request.MaxAudioBitDepth,
- TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
- };
-
- if (isHeadRequest)
- {
- return await service.Head(newRequest).ConfigureAwait(false);
- }
- return await service.Get(newRequest).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 9f37bb70a..aef16e442 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Querying;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
@@ -192,8 +193,10 @@ namespace MediaBrowser.Api
var dtoOptions = GetDtoOptions(_authContext, request);
- var dtos = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user)
+ .ConfigureAwait(false));
+ var dtos = returnList
+ .ToArray(returnList.Count);
var index = 0;
foreach (var item in dtos)
diff --git a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
index 9c3dde6a4..6b10fcb05 100644
--- a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
+++ b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
@@ -458,7 +458,7 @@ namespace MediaBrowser.Api.Reports
break;
case HeaderMetadata.Network:
- option.Column = (i, r) => this.GetListAsString(i.Studios);
+ option.Column = (i, r) => this.GetListAsString(i.Studios.ToList());
option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault());
option.Header.ItemViewType = ItemViewType.ItemByNameDetails;
option.Header.SortField = "Studio,SortName";
@@ -513,7 +513,7 @@ namespace MediaBrowser.Api.Reports
internalHeader = HeaderMetadata.AlbumArtist;
break;
case HeaderMetadata.AudioAlbumArtist:
- option.Column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists));
+ option.Column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists.ToList()));
option.Header.SortField = "AlbumArtist,Album,SortName";
internalHeader = HeaderMetadata.AlbumArtist;
break;
@@ -613,7 +613,7 @@ namespace MediaBrowser.Api.Reports
HasImageTagsPrimary = item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0,
HasImageTagsBackdrop = item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0,
HasImageTagsLogo = item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Logo) > 0,
- HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Count > 0 : false,
+ HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Length > 0 : false,
HasSubtitles = video != null ? video.HasSubtitles : false,
RowType = ReportHelper.GetRowType(item.GetClientTypeName())
};
diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs
index d4201e73c..76d282990 100644
--- a/MediaBrowser.Api/Reports/ReportsService.cs
+++ b/MediaBrowser.Api/Reports/ReportsService.cs
@@ -183,7 +183,6 @@ namespace MediaBrowser.Api.Reports
Limit = request.Limit,
StartIndex = request.StartIndex,
IsMissing = request.IsMissing,
- IsVirtualUnaired = request.IsVirtualUnaired,
IsUnaired = request.IsUnaired,
CollapseBoxSetItems = request.CollapseBoxSetItems,
NameLessThan = request.NameLessThan,
@@ -283,12 +282,6 @@ namespace MediaBrowser.Api.Reports
query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray();
}
- // Filter by Series AirDays
- if (!string.IsNullOrEmpty(request.AirDays))
- {
- query.AirDays = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)).ToArray();
- }
-
// ExcludeLocationTypes
if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
{
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 80a703313..77e29d6cc 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -159,9 +159,9 @@ namespace MediaBrowser.Api
IncludeStudios = request.IncludeStudios,
StartIndex = request.StartIndex,
UserId = request.UserId,
- IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
- ExcludeItemTypes = (request.ExcludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
- MediaTypes = (request.MediaTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
+ IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true),
+ ExcludeItemTypes = ApiEntryPoint.Split(request.ExcludeItemTypes, ',', true),
+ MediaTypes = ApiEntryPoint.Split(request.MediaTypes, ',', true),
ParentId = request.ParentId,
IsKids = request.IsKids,
@@ -198,7 +198,6 @@ namespace MediaBrowser.Api
Type = item.GetClientTypeName(),
MediaType = item.MediaType,
MatchedTerm = hintInfo.MatchedTerm,
- DisplayMediaType = item.DisplayMediaType,
RunTimeTicks = item.RunTimeTicks,
ProductionYear = item.ProductionYear,
ChannelId = item.ChannelId,
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 358d09c18..fe40ceeeb 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -477,7 +477,7 @@ namespace MediaBrowser.Api.Session
{
var command = new PlayRequest
{
- ItemIds = request.ItemIds.Split(',').ToArray(),
+ ItemIds = request.ItemIds.Split(','),
PlayCommand = request.PlayCommand,
StartPositionTicks = request.StartPositionTicks
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 5463ae982..a4b14d2d4 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -11,6 +11,7 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
@@ -80,7 +81,7 @@ namespace MediaBrowser.Api
var query = new InternalItemsQuery(user)
{
- IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
+ IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(includeTypes.Length),
Recursive = true,
DtoOptions = dtoOptions
};
@@ -107,7 +108,7 @@ namespace MediaBrowser.Api
return new QueryResult<BaseItemDto>
{
- Items = dtos.ToArray(),
+ Items = dtos.ToArray(dtos.Count),
TotalRecordCount = items.Count
};
@@ -142,11 +143,6 @@ namespace MediaBrowser.Api
return item.Tags;
}
- private static IEnumerable<string> GetKeywords(BaseItem item)
- {
- return item.Keywords;
- }
-
/// <summary>
/// Gets the similiarity score.
/// </summary>
@@ -170,9 +166,6 @@ namespace MediaBrowser.Api
// Find common tags
points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
- // Find common keywords
- points += GetKeywords(item1).Where(i => GetKeywords(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
-
// Find common studios
points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index cd56b69bd..7d612a796 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -95,7 +95,6 @@ namespace MediaBrowser.Api
config.EnableStandaloneMusicKeys = true;
config.EnableCaseSensitiveItemIds = true;
config.SkipDeserializationForBasicTypes = true;
- config.SkipDeserializationForAudio = true;
config.EnableLocalizedGuids = true;
config.EnableSimpleArtistDetection = true;
config.EnableNormalizedItemByNameIds = true;
diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs
index 99411ffdc..2456dd6c0 100644
--- a/MediaBrowser.Api/SuggestionsService.cs
+++ b/MediaBrowser.Api/SuggestionsService.cs
@@ -8,6 +8,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
@@ -71,7 +72,7 @@ namespace MediaBrowser.Api
return new QueryResult<BaseItemDto>
{
TotalRecordCount = result.TotalRecordCount,
- Items = dtoList.ToArray()
+ Items = dtoList.ToArray(dtoList.Count)
};
}
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index a9cec0914..cbff7cc2e 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -118,12 +118,11 @@ namespace MediaBrowser.Api.System
public object Get(GetServerLogs request)
{
- List<FileSystemMetadata> files;
+ IEnumerable<FileSystemMetadata> files;
try
{
- files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath, new[] { ".txt" }, true, false)
- .ToList();
+ files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath, new[] { ".txt" }, true, false);
}
catch (IOException)
{
diff --git a/MediaBrowser.Api/TestService.cs b/MediaBrowser.Api/TestService.cs
deleted file mode 100644
index 5340b816c..000000000
--- a/MediaBrowser.Api/TestService.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api
-{
- [Route("/Test/String", "GET")]
- public class GetString
- {
- }
-
- [Route("/Test/OptimizedString", "GET")]
- public class GetOptimizedString
- {
- }
-
- [Route("/Test/Bytes", "GET")]
- public class GetBytes
- {
- }
-
- [Route("/Test/OptimizedBytes", "GET")]
- public class GetOptimizedBytes
- {
- }
-
- [Route("/Test/Stream", "GET")]
- public class GetStream
- {
- }
-
- [Route("/Test/OptimizedStream", "GET")]
- public class GetOptimizedStream
- {
- }
-
- [Route("/Test/BytesWithContentType", "GET")]
- public class GetBytesWithContentType
- {
- }
-
- public class TestService : BaseApiService
- {
- public object Get(GetString request)
- {
- return "Welcome to Emby!";
- }
- public object Get(GetOptimizedString request)
- {
- return ToOptimizedResult("Welcome to Emby!");
- }
- public object Get(GetBytes request)
- {
- return Encoding.UTF8.GetBytes("Welcome to Emby!");
- }
- public object Get(GetOptimizedBytes request)
- {
- return ToOptimizedResult(Encoding.UTF8.GetBytes("Welcome to Emby!"));
- }
- public object Get(GetBytesWithContentType request)
- {
- return ApiEntryPoint.Instance.ResultFactory.GetResult(Encoding.UTF8.GetBytes("Welcome to Emby!"), "text/html");
- }
- public object Get(GetStream request)
- {
- return new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!"));
- }
- public object Get(GetOptimizedStream request)
- {
- return ToOptimizedResult(new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!")));
- }
- }
-}
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index 5a6004760..148e65b49 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -14,6 +14,7 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
@@ -165,9 +166,6 @@ namespace MediaBrowser.Api
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
- [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsVirtualUnaired { get; set; }
-
[ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AdjacentTo { get; set; }
@@ -199,6 +197,12 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
+
+ [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string SortBy { get; set; }
+
+ [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public SortOrder? SortOrder { get; set; }
}
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
@@ -227,9 +231,6 @@ namespace MediaBrowser.Api
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
- [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsVirtualUnaired { get; set; }
-
[ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AdjacentTo { get; set; }
@@ -320,11 +321,13 @@ namespace MediaBrowser.Api
SimilarTo = item,
DtoOptions = dtoOptions
- }).ToList();
+ });
+
+ var returnList = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false));
var result = new QueryResult<BaseItemDto>
{
- Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
+ Items = returnList.ToArray(returnList.Count),
TotalRecordCount = itemsResult.Count
};
@@ -354,9 +357,10 @@ namespace MediaBrowser.Api
Recursive = true,
DtoOptions = options
- }).ToList();
+ });
- var returnItems = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false));
+ var returnItems = returnList.ToArray(returnList.Count);
var result = new ItemsResult
{
@@ -388,7 +392,8 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.UserId);
- var returnItems = (await _dtoService.GetBaseItemDtos(result.Items, options, user).ConfigureAwait(false)).ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(result.Items, options, user).ConfigureAwait(false));
+ var returnItems = returnList.ToArray(returnList.Count);
return ToOptimizedSerializedResultUsingCache(new ItemsResult
{
@@ -432,19 +437,18 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException("Series not found");
}
- var seasons = (series.GetItems(new InternalItemsQuery(user)
+ var seasons = (series.GetItemList(new InternalItemsQuery(user)
{
IsMissing = request.IsMissing,
- IsVirtualUnaired = request.IsVirtualUnaired,
IsSpecialSeason = request.IsSpecialSeason,
AdjacentTo = request.AdjacentTo
- })).Items.OfType<Season>();
+ })).OfType<Season>();
var dtoOptions = GetDtoOptions(_authContext, request);
- var returnItems = (await _dtoService.GetBaseItemDtos(seasons, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnList = (await _dtoService.GetBaseItemDtos(seasons, dtoOptions, user).ConfigureAwait(false));
+ var returnItems = returnList.ToArray(returnList.Count);
return new ItemsResult
{
@@ -521,13 +525,6 @@ namespace MediaBrowser.Api
episodes = episodes.Where(i => i.IsMissingEpisode == val);
}
- // Filter after the fact in case the ui doesn't want them
- if (request.IsVirtualUnaired.HasValue)
- {
- var val = request.IsVirtualUnaired.Value;
- episodes = episodes.Where(i => i.IsVirtualUnaired == val);
- }
-
if (!string.IsNullOrWhiteSpace(request.StartItemId))
{
episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N"), request.StartItemId, StringComparison.OrdinalIgnoreCase));
@@ -541,12 +538,17 @@ namespace MediaBrowser.Api
returnItems = UserViewBuilder.FilterForAdjacency(returnItems, request.AdjacentTo);
}
+ if (string.Equals(request.SortBy, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
+ {
+ returnItems = returnItems.OrderBy(i => Guid.NewGuid());
+ }
+
var returnList = returnItems.ToList();
var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit);
- var dtos = (await _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
+ var returnDtos = (await _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ConfigureAwait(false));
+ var dtos = returnDtos.ToArray(returnDtos.Count);
return new ItemsResult
{
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 24d0a7d52..30e64d89d 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -10,6 +10,7 @@ using System.Linq;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.UserLibrary
{
@@ -210,7 +211,7 @@ namespace MediaBrowser.Api.UserLibrary
return new ItemsResult
{
- Items = dtos.ToArray(),
+ Items = dtos.ToArray(result.Items.Length),
TotalRecordCount = result.TotalRecordCount
};
}
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 3415d01f1..a9c5ae700 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -70,9 +70,6 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsUnaired { get; set; }
- [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsVirtualUnaired { get; set; }
-
[ApiMember(Name = "MinCommunityRating", Description = "Optional filter by minimum community rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public double? MinCommunityRating { get; set; }
@@ -300,13 +297,6 @@ namespace MediaBrowser.Api.UserLibrary
public string VideoTypes { get; set; }
/// <summary>
- /// Gets or sets the air days.
- /// </summary>
- /// <value>The air days.</value>
- [ApiMember(Name = "AirDays", Description = "Optional filter by Series Air Days. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string AirDays { get; set; }
-
- /// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
@@ -461,7 +451,7 @@ namespace MediaBrowser.Api.UserLibrary
/// Gets the image types.
/// </summary>
/// <returns>IEnumerable{ImageType}.</returns>
- public IEnumerable<ImageType> GetImageTypes()
+ public ImageType[] GetImageTypes()
{
var val = ImageTypes;
@@ -470,7 +460,7 @@ namespace MediaBrowser.Api.UserLibrary
return new ImageType[] { };
}
- return val.Split(',').Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true));
+ return val.Split(',').Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToArray();
}
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 01e1ce769..f3d7772fc 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.UserLibrary
{
@@ -127,7 +128,7 @@ namespace MediaBrowser.Api.UserLibrary
return new ItemsResult
{
TotalRecordCount = result.TotalRecordCount,
- Items = dtoList.ToArray()
+ Items = dtoList.ToArray(dtoList.Count)
};
}
@@ -206,7 +207,6 @@ namespace MediaBrowser.Api.UserLibrary
Limit = request.Limit,
StartIndex = request.StartIndex,
IsMissing = request.IsMissing,
- IsVirtualUnaired = request.IsVirtualUnaired,
IsUnaired = request.IsUnaired,
CollapseBoxSetItems = request.CollapseBoxSetItems,
NameLessThan = request.NameLessThan,
@@ -238,8 +238,8 @@ namespace MediaBrowser.Api.UserLibrary
PersonIds = request.GetPersonIds(),
PersonTypes = request.GetPersonTypes(),
Years = request.GetYears(),
- ImageTypes = request.GetImageTypes().ToArray(),
- VideoTypes = request.GetVideoTypes().ToArray(),
+ ImageTypes = request.GetImageTypes(),
+ VideoTypes = request.GetVideoTypes(),
AdjacentTo = request.AdjacentTo,
ItemIds = request.GetItemIds(),
MinPlayers = request.MinPlayers,
@@ -319,12 +319,6 @@ namespace MediaBrowser.Api.UserLibrary
query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray();
}
- // Filter by Series AirDays
- if (!string.IsNullOrEmpty(request.AirDays))
- {
- query.AirDays = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)).ToArray();
- }
-
// ExcludeLocationTypes
if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
{
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
deleted file mode 100644
index 98b4a5d5d..000000000
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ /dev/null
@@ -1,451 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Session;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.UserLibrary
-{
- /// <summary>
- /// Class MarkPlayedItem
- /// </summary>
- [Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")]
- public class MarkPlayedItem : IReturn<UserItemDataDto>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string UserId { get; set; }
-
- [ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any). Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string DatePlayed { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
- }
-
- /// <summary>
- /// Class MarkUnplayedItem
- /// </summary>
- [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")]
- public class MarkUnplayedItem : IReturn<UserItemDataDto>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Sessions/Playing", "POST", Summary = "Reports playback has started within a session")]
- public class ReportPlaybackStart : PlaybackStartInfo, IReturnVoid
- {
- }
-
- [Route("/Sessions/Playing/Progress", "POST", Summary = "Reports playback progress within a session")]
- public class ReportPlaybackProgress : PlaybackProgressInfo, IReturnVoid
- {
- }
-
- [Route("/Sessions/Playing/Ping", "POST", Summary = "Pings a playback session")]
- public class PingPlaybackSession : IReturnVoid
- {
- [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string PlaySessionId { get; set; }
- }
-
- [Route("/Sessions/Playing/Stopped", "POST", Summary = "Reports playback has stopped within a session")]
- public class ReportPlaybackStopped : PlaybackStopInfo, IReturnVoid
- {
- }
-
- /// <summary>
- /// Class OnPlaybackStart
- /// </summary>
- [Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")]
- public class OnPlaybackStart : IReturnVoid
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
-
- [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; }
-
- [ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? AudioStreamIndex { get; set; }
-
- [ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? SubtitleStreamIndex { get; set; }
-
- [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public PlayMethod PlayMethod { get; set; }
-
- [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string LiveStreamId { get; set; }
-
- [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string PlaySessionId { get; set; }
- }
-
- /// <summary>
- /// Class OnPlaybackProgress
- /// </summary>
- [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")]
- public class OnPlaybackProgress : IReturnVoid
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
-
- [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 the position ticks.
- /// </summary>
- /// <value>The position ticks.</value>
- [ApiMember(Name = "PositionTicks", Description = "Optional. The current position, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public long? PositionTicks { get; set; }
-
- [ApiMember(Name = "IsPaused", Description = "Indicates if the player is paused.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
- public bool IsPaused { get; set; }
-
- [ApiMember(Name = "IsMuted", Description = "Indicates if the player is muted.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
- public bool IsMuted { get; set; }
-
- [ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? AudioStreamIndex { get; set; }
-
- [ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? SubtitleStreamIndex { get; set; }
-
- [ApiMember(Name = "VolumeLevel", Description = "Scale of 0-100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? VolumeLevel { get; set; }
-
- [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public PlayMethod PlayMethod { get; set; }
-
- [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string LiveStreamId { get; set; }
-
- [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string PlaySessionId { get; set; }
-
- [ApiMember(Name = "RepeatMode", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public RepeatMode RepeatMode { get; set; }
- }
-
- /// <summary>
- /// Class OnPlaybackStopped
- /// </summary>
- [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")]
- public class OnPlaybackStopped : IReturnVoid
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
-
- [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string MediaSourceId { get; set; }
-
- [ApiMember(Name = "NextMediaType", Description = "The next media type that will play", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string NextMediaType { get; set; }
-
- /// <summary>
- /// Gets or sets the position ticks.
- /// </summary>
- /// <value>The position ticks.</value>
- [ApiMember(Name = "PositionTicks", Description = "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")]
- public long? PositionTicks { get; set; }
-
- [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string LiveStreamId { get; set; }
-
- [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string PlaySessionId { get; set; }
- }
-
- [Authenticated]
- public class PlaystateService : BaseApiService
- {
- private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataRepository;
- private readonly ILibraryManager _libraryManager;
- private readonly ISessionManager _sessionManager;
- private readonly ISessionContext _sessionContext;
- private readonly IAuthorizationContext _authContext;
-
- public PlaystateService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, ISessionContext sessionContext, IAuthorizationContext authContext)
- {
- _userManager = userManager;
- _userDataRepository = userDataRepository;
- _libraryManager = libraryManager;
- _sessionManager = sessionManager;
- _sessionContext = sessionContext;
- _authContext = authContext;
- }
-
- /// <summary>
- /// Posts the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public async Task<object> Post(MarkPlayedItem request)
- {
- var result = await MarkPlayed(request).ConfigureAwait(false);
-
- return ToOptimizedResult(result);
- }
-
- private async Task<UserItemDataDto> MarkPlayed(MarkPlayedItem request)
- {
- var user = _userManager.GetUserById(request.UserId);
-
- DateTime? datePlayed = null;
-
- if (!string.IsNullOrEmpty(request.DatePlayed))
- {
- datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
- }
-
- var session = await GetSession(_sessionContext).ConfigureAwait(false);
-
- var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false);
-
- foreach (var additionalUserInfo in session.AdditionalUsers)
- {
- var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
-
- await UpdatePlayedStatus(additionalUser, request.Id, true, datePlayed).ConfigureAwait(false);
- }
-
- return dto;
- }
-
- private PlayMethod ValidatePlayMethod(PlayMethod method, string playSessionId)
- {
- if (method == PlayMethod.Transcode)
- {
- var job = string.IsNullOrWhiteSpace(playSessionId) ? null : ApiEntryPoint.Instance.GetTranscodingJob(playSessionId);
- if (job == null)
- {
- return PlayMethod.DirectPlay;
- }
- }
-
- return method;
- }
-
- /// <summary>
- /// Posts the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public void Post(OnPlaybackStart request)
- {
- Post(new ReportPlaybackStart
- {
- CanSeek = request.CanSeek,
- ItemId = request.Id,
- MediaSourceId = request.MediaSourceId,
- AudioStreamIndex = request.AudioStreamIndex,
- SubtitleStreamIndex = request.SubtitleStreamIndex,
- PlayMethod = request.PlayMethod,
- PlaySessionId = request.PlaySessionId,
- LiveStreamId = request.LiveStreamId
- });
- }
-
- public void Post(ReportPlaybackStart request)
- {
- request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId);
-
- request.SessionId = GetSession(_sessionContext).Result.Id;
-
- var task = _sessionManager.OnPlaybackStart(request);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Posts the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public void Post(OnPlaybackProgress request)
- {
- Post(new ReportPlaybackProgress
- {
- ItemId = request.Id,
- PositionTicks = request.PositionTicks,
- IsMuted = request.IsMuted,
- IsPaused = request.IsPaused,
- MediaSourceId = request.MediaSourceId,
- AudioStreamIndex = request.AudioStreamIndex,
- SubtitleStreamIndex = request.SubtitleStreamIndex,
- VolumeLevel = request.VolumeLevel,
- PlayMethod = request.PlayMethod,
- PlaySessionId = request.PlaySessionId,
- LiveStreamId = request.LiveStreamId,
- RepeatMode = request.RepeatMode
- });
- }
-
- public void Post(ReportPlaybackProgress request)
- {
- request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId);
-
- request.SessionId = GetSession(_sessionContext).Result.Id;
-
- var task = _sessionManager.OnPlaybackProgress(request);
-
- Task.WaitAll(task);
- }
-
- public void Post(PingPlaybackSession request)
- {
- ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId, null);
- }
-
- /// <summary>
- /// Posts the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public void Delete(OnPlaybackStopped request)
- {
- Post(new ReportPlaybackStopped
- {
- ItemId = request.Id,
- PositionTicks = request.PositionTicks,
- MediaSourceId = request.MediaSourceId,
- PlaySessionId = request.PlaySessionId,
- LiveStreamId = request.LiveStreamId,
- NextMediaType = request.NextMediaType
- });
- }
-
- public void Post(ReportPlaybackStopped request)
- {
- Logger.Debug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty);
-
- if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
- {
- ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
- }
-
- request.SessionId = GetSession(_sessionContext).Result.Id;
-
- var task = _sessionManager.OnPlaybackStopped(request);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Deletes the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public object Delete(MarkUnplayedItem request)
- {
- var task = MarkUnplayed(request);
-
- return ToOptimizedResult(task.Result);
- }
-
- private async Task<UserItemDataDto> MarkUnplayed(MarkUnplayedItem request)
- {
- var user = _userManager.GetUserById(request.UserId);
-
- var session = await GetSession(_sessionContext).ConfigureAwait(false);
-
- var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false);
-
- foreach (var additionalUserInfo in session.AdditionalUsers)
- {
- var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
-
- await UpdatePlayedStatus(additionalUser, request.Id, false, null).ConfigureAwait(false);
- }
-
- return dto;
- }
-
- /// <summary>
- /// Updates the played status.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="itemId">The item id.</param>
- /// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
- /// <param name="datePlayed">The date played.</param>
- /// <returns>Task.</returns>
- private async Task<UserItemDataDto> UpdatePlayedStatus(User user, string itemId, bool wasPlayed, DateTime? datePlayed)
- {
- var item = _libraryManager.GetItemById(itemId);
-
- if (wasPlayed)
- {
- await item.MarkPlayed(user, datePlayed, true).ConfigureAwait(false);
- }
- else
- {
- await item.MarkUnplayed(user).ConfigureAwait(false);
- }
-
- return _userDataRepository.GetUserDataDto(item, user);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 4bb3de882..ee1162687 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api.UserLibrary
{
@@ -312,7 +313,7 @@ namespace MediaBrowser.Api.UserLibrary
var list = _userViewManager.GetLatestItems(new LatestItemsQuery
{
GroupItems = request.GroupItems,
- IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
+ IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true),
IsPlayed = request.IsPlayed,
Limit = request.Limit,
ParentId = request.ParentId,
@@ -486,8 +487,7 @@ namespace MediaBrowser.Api.UserLibrary
var dtoOptions = GetDtoOptions(_authContext, request);
- var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
- .ToArray();
+ var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
var result = new ItemsResult
{
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index 729b50c1f..57d3d7e39 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -126,7 +126,7 @@ namespace MediaBrowser.Api
await link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
- video.LinkedAlternateVersions.Clear();
+ video.LinkedAlternateVersions = Video.EmptyLinkedChildArray;
await video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
@@ -185,19 +185,23 @@ namespace MediaBrowser.Api
}).First();
}
+ var list = primaryVersion.LinkedAlternateVersions.ToList();
+
foreach (var item in items.Where(i => i.Id != primaryVersion.Id))
{
item.PrimaryVersionId = primaryVersion.Id.ToString("N");
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
- primaryVersion.LinkedAlternateVersions.Add(new LinkedChild
+ list.Add(new LinkedChild
{
Path = item.Path,
ItemId = item.Id
});
}
+ primaryVersion.LinkedAlternateVersions = list.ToArray();
+
await primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
index 05517ebcd..85feec40b 100644
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs
@@ -1,7 +1,5 @@
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Chapters
@@ -21,10 +19,6 @@ namespace MediaBrowser.Controller.Chapters
/// <summary>
/// Saves the chapters.
/// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="chapters">The chapters.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken);
+ Task SaveChapters(string itemId, List<ChapterInfo> chapters);
}
}
diff --git a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs
index 160a788f1..38d2611f0 100644
--- a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs
+++ b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs
@@ -8,7 +8,6 @@ namespace MediaBrowser.Controller.Collections
public ManualCollectionsFolder()
{
Name = "Collections";
- DisplayMediaType = "CollectionFolder";
}
public override bool IsHidden
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index 2832462a8..29363f492 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns>IEnumerable{IImageEnhancer}.</returns>
- IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType);
+ IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType);
/// <summary>
/// Gets the image cache tag.
@@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="item">The item.</param>
/// <param name="image">The image.</param>
/// <returns>Guid.</returns>
- string GetImageCacheTag(IHasImages item, ItemImageInfo image);
+ string GetImageCacheTag(IHasMetadata item, ItemImageInfo image);
/// <summary>
/// Gets the image cache tag.
@@ -71,7 +71,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="image">The image.</param>
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
- string GetImageCacheTag(IHasImages item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers);
+ string GetImageCacheTag(IHasMetadata item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers);
/// <summary>
/// Processes the image.
@@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{System.String}.</returns>
- Task<string> GetEnhancedImage(IHasImages item, ImageType imageType, int imageIndex);
+ Task<string> GetEnhancedImage(IHasMetadata item, ImageType imageType, int imageIndex);
/// <summary>
/// Gets the supported image output formats.
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
index 54f2ff987..9452446a1 100644
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Controller.Drawing
return new ImageSize(widthValue, height);
}
- private static double GetEstimatedAspectRatio(ImageType type, IHasImages item)
+ private static double GetEstimatedAspectRatio(ImageType type, IHasMetadata item)
{
switch (type)
{
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
index cfb3a97ee..fac21c744 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Drawing
{
public string ItemId { get; set; }
public string ItemType { get; set; }
- public IHasImages Item { get; set; }
+ public IHasMetadata Item { get; set; }
public ItemImageInfo Image { get; set; }
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
index c5601c49f..5dfa94e1e 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
@@ -5,12 +5,12 @@ namespace MediaBrowser.Controller.Drawing
{
public static class ImageProcessorExtensions
{
- public static string GetImageCacheTag(this IImageProcessor processor, IHasImages item, ImageType imageType)
+ public static string GetImageCacheTag(this IImageProcessor processor, IHasMetadata item, ImageType imageType)
{
return processor.GetImageCacheTag(item, imageType, 0);
}
- public static string GetImageCacheTag(this IImageProcessor processor, IHasImages item, ImageType imageType, int imageIndex)
+ public static string GetImageCacheTag(this IImageProcessor processor, IHasMetadata item, ImageType imageType, int imageIndex)
{
var imageInfo = item.GetImageInfo(imageType, imageIndex);
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index a6f807ce9..963092f52 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -24,14 +24,14 @@ namespace MediaBrowser.Controller.Dto
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
- void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
+ void AttachPrimaryImageAspectRatio(IItemDto dto, IHasMetadata item);
/// <summary>
/// Gets the primary image aspect ratio.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.Nullable&lt;System.Double&gt;.</returns>
- double? GetPrimaryImageAspectRatio(IHasImages item);
+ double? GetPrimaryImageAspectRatio(IHasMetadata item);
/// <summary>
/// Gets the base item dto.
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index e8ebbdb70..f88522f78 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -23,18 +23,6 @@ namespace MediaBrowser.Controller.Entities
PhysicalLocationsList = new List<string>();
}
- /// <summary>
- /// We don't support manual shortcuts
- /// </summary>
- [IgnoreDataMember]
- protected override bool SupportsShortcutChildren
- {
- get
- {
- return false;
- }
- }
-
[IgnoreDataMember]
public override bool IsPhysicalRoot
{
@@ -80,9 +68,9 @@ namespace MediaBrowser.Controller.Entities
public List<string> PhysicalLocationsList { get; set; }
- protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
+ protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
{
- return CreateResolveArgs(directoryService, true).FileSystemChildren;
+ return CreateResolveArgs(directoryService, true).FileSystemChildren.ToArray();
}
private List<Guid> _childrenIds = null;
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 873ac3104..0781dc35b 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -9,6 +9,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.Audio
@@ -27,9 +28,11 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets or sets the artist.
/// </summary>
/// <value>The artist.</value>
+ [IgnoreDataMember]
public List<string> Artists { get; set; }
- public List<string> AlbumArtists { get; set; }
+ [IgnoreDataMember]
+ public string[] AlbumArtists { get; set; }
[IgnoreDataMember]
public override bool EnableRefreshOnDateModifiedChange
@@ -40,7 +43,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public Audio()
{
Artists = new List<string>();
- AlbumArtists = new List<string>();
+ AlbumArtists = EmptyStringArray;
}
public override double? GetDefaultPrimaryImageAspectRatio()
@@ -193,6 +196,23 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.GetBlockUnratedType();
}
+ public List<MediaStream> GetMediaStreams()
+ {
+ return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = Id
+ });
+ }
+
+ public List<MediaStream> GetMediaStreams(MediaStreamType type)
+ {
+ return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = Id,
+ Type = type
+ });
+ }
+
public SongInfo GetLookupInfo()
{
var info = GetItemLookupInfo<SongInfo>();
@@ -204,7 +224,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return info;
}
- public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+ public virtual List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
if (SourceType == SourceType.Channel)
{
@@ -248,7 +268,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
Id = i.Id.ToString("N"),
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
- MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(),
+ MediaStreams = MediaSourceManager.GetMediaStreams(i.Id),
Name = i.Name,
Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks,
diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
index a7c914664..6900699e5 100644
--- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
@@ -5,7 +5,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
public interface IHasAlbumArtist
{
- List<string> AlbumArtists { get; set; }
+ string[] AlbumArtists { get; set; }
}
public interface IHasArtist
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 516ab5053..c35e81826 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -19,13 +19,13 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
{
- public List<string> AlbumArtists { get; set; }
+ public string[] AlbumArtists { get; set; }
public List<string> Artists { get; set; }
public MusicAlbum()
{
Artists = new List<string>();
- AlbumArtists = new List<string>();
+ AlbumArtists = EmptyStringArray;
}
[IgnoreDataMember]
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 7a37b2e02..559806ac4 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -212,7 +212,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = GetRecursiveChildren().ToList();
+ var items = GetRecursiveChildren();
var songs = items.OfType<Audio>().ToList();
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index b4a3d89ea..8cc90db7d 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -39,21 +39,26 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class BaseItem
/// </summary>
- public abstract class BaseItem : IHasProviderIds, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo<ItemLookupInfo>
+ public abstract class BaseItem : IHasMetadata, IHasLookupInfo<ItemLookupInfo>
{
+ protected static Guid[] EmptyGuidArray = new Guid[] { };
+ protected static MetadataFields[] EmptyMetadataFieldsArray = new MetadataFields[] { };
+ protected static string[] EmptyStringArray = new string[] { };
+ protected static MediaUrl[] EmptyMediaUrlArray = new MediaUrl[] { };
+ protected static ItemImageInfo[] EmptyItemImageInfoArray = new ItemImageInfo[] { };
+ public static readonly LinkedChild[] EmptyLinkedChildArray = new LinkedChild[] { };
+
protected BaseItem()
{
- ThemeSongIds = new List<Guid>();
- ThemeVideoIds = new List<Guid>();
- Keywords = new List<string>();
- Tags = new List<string>();
+ ThemeSongIds = EmptyGuidArray;
+ ThemeVideoIds = EmptyGuidArray;
+ Tags = EmptyStringArray;
Genres = new List<string>();
- Studios = new List<string>();
+ Studios = EmptyStringArray;
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- LockedFields = new List<MetadataFields>();
- ImageInfos = new List<ItemImageInfo>();
- InheritedTags = new List<string>();
- ProductionLocations = new List<string>();
+ LockedFields = EmptyMetadataFieldsArray;
+ ImageInfos = EmptyItemImageInfoArray;
+ ProductionLocations = EmptyStringArray;
}
public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
@@ -74,8 +79,10 @@ namespace MediaBrowser.Controller.Entities
public static string ThemeSongFilename = "theme";
public static string ThemeVideosFolderName = "backdrops";
- public List<Guid> ThemeSongIds { get; set; }
- public List<Guid> ThemeVideoIds { get; set; }
+ [IgnoreDataMember]
+ public Guid[] ThemeSongIds { get; set; }
+ [IgnoreDataMember]
+ public Guid[] ThemeVideoIds { get; set; }
[IgnoreDataMember]
public string PreferredMetadataCountryCode { get; set; }
@@ -88,7 +95,8 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public string Tagline { get; set; }
- public List<ItemImageInfo> ImageInfos { get; set; }
+ [IgnoreDataMember]
+ public ItemImageInfo[] ImageInfos { get; set; }
[IgnoreDataMember]
public bool IsVirtualItem { get; set; }
@@ -130,12 +138,6 @@ namespace MediaBrowser.Controller.Entities
public bool IsInMixedFolder { get; set; }
[IgnoreDataMember]
- protected virtual bool SupportsIsInMixedFolderDetection
- {
- get { return false; }
- }
-
- [IgnoreDataMember]
public virtual bool SupportsPlayedStatus
{
get
@@ -153,16 +155,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- public bool DetectIsInMixedFolder()
- {
- if (SupportsIsInMixedFolderDetection)
- {
-
- }
-
- return IsInMixedFolder;
- }
-
[IgnoreDataMember]
public virtual bool SupportsRemoteImageDownloading
{
@@ -214,7 +206,9 @@ namespace MediaBrowser.Controller.Entities
get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; }
}
+ [IgnoreDataMember]
public int? TotalBitrate { get; set; }
+ [IgnoreDataMember]
public ExtraType? ExtraType { get; set; }
[IgnoreDataMember]
@@ -560,7 +554,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The locked fields.</value>
[IgnoreDataMember]
- public List<MetadataFields> LockedFields { get; set; }
+ public MetadataFields[] LockedFields { get; set; }
/// <summary>
/// Gets the type of the media.
@@ -820,13 +814,6 @@ namespace MediaBrowser.Controller.Entities
public DateTime? EndDate { get; set; }
/// <summary>
- /// Gets or sets the display type of the media.
- /// </summary>
- /// <value>The display type of the media.</value>
- [IgnoreDataMember]
- public string DisplayMediaType { get; set; }
-
- /// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
@@ -836,9 +823,6 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public int InheritedParentalRatingValue { get; set; }
- [IgnoreDataMember]
- public List<string> InheritedTags { get; set; }
-
/// <summary>
/// Gets or sets the critic rating.
/// </summary>
@@ -865,7 +849,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The studios.</value>
[IgnoreDataMember]
- public List<string> Studios { get; set; }
+ public string[] Studios { get; set; }
/// <summary>
/// Gets or sets the genres.
@@ -879,10 +863,10 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The tags.</value>
[IgnoreDataMember]
- public List<string> Tags { get; set; }
+ public string[] Tags { get; set; }
- public List<string> Keywords { get; set; }
- public List<string> ProductionLocations { get; set; }
+ [IgnoreDataMember]
+ public string[] ProductionLocations { get; set; }
/// <summary>
/// Gets or sets the home page URL.
@@ -991,11 +975,11 @@ namespace MediaBrowser.Controller.Entities
/// Loads the theme songs.
/// </summary>
/// <returns>List{Audio.Audio}.</returns>
- private static IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+ private static Audio.Audio[] LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => directoryService.GetFiles(i.FullName))
+ .SelectMany(i => FileSystem.GetFiles(i.FullName))
.ToList();
// Support plex/xbmc convention
@@ -1020,18 +1004,18 @@ namespace MediaBrowser.Controller.Entities
return audio;
// Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
+ }).OrderBy(i => i.Path).ToArray();
}
/// <summary>
/// Loads the video backdrops.
/// </summary>
/// <returns>List{Video}.</returns>
- private static IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+ private static Video[] LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => directoryService.GetFiles(i.FullName));
+ .SelectMany(i => FileSystem.GetFiles(i.FullName));
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
.OfType<Video>()
@@ -1050,7 +1034,7 @@ namespace MediaBrowser.Controller.Entities
return item;
// Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
+ }).OrderBy(i => i.Path).ToArray();
}
public Task RefreshMetadata(CancellationToken cancellationToken)
@@ -1158,7 +1142,7 @@ namespace MediaBrowser.Controller.Entities
{
if (SupportsThemeMedia)
{
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
@@ -1176,7 +1160,7 @@ namespace MediaBrowser.Controller.Entities
return themeSongsChanged || themeVideosChanged || localTrailersChanged;
}
- protected virtual IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
+ protected virtual FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
{
var path = ContainingFolderPath;
@@ -1187,7 +1171,7 @@ namespace MediaBrowser.Controller.Entities
{
var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
- var newItemIds = newItems.Select(i => i.Id).ToList();
+ var newItemIds = newItems.Select(i => i.Id).ToArray();
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
@@ -1202,9 +1186,9 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
+ var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService);
- var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
+ var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToArray(newThemeVideos.Length);
var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
@@ -1233,8 +1217,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
private async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
- var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
+ var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService);
+ var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToArray(newThemeSongs.Length);
var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
@@ -1313,12 +1297,9 @@ namespace MediaBrowser.Controller.Entities
{
var current = this;
- if (!SupportsIsInMixedFolderDetection)
+ if (current.IsInMixedFolder != newItem.IsInMixedFolder)
{
- if (current.IsInMixedFolder != newItem.IsInMixedFolder)
- {
- return false;
- }
+ return false;
}
return true;
@@ -1739,12 +1720,28 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("name");
}
- if (!Studios.Contains(name, StringComparer.OrdinalIgnoreCase))
+ var current = Studios;
+
+ if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
{
- Studios.Add(name);
+ if (current.Length == 0)
+ {
+ Studios = new[] { name };
+ }
+ else
+ {
+ var list = current.ToArray(current.Length + 1);
+ list[list.Length - 1] = name;
+ Studios = list;
+ }
}
}
+ public void SetStudios(IEnumerable<string> names)
+ {
+ Studios = names.Distinct().ToArray();
+ }
+
/// <summary>
/// Adds a genre to the item
/// </summary>
@@ -1864,10 +1861,18 @@ namespace MediaBrowser.Controller.Entities
if (existingImage != null)
{
- ImageInfos.Remove(existingImage);
+ existingImage.Path = image.Path;
+ existingImage.DateModified = image.DateModified;
+ existingImage.IsPlaceholder = image.IsPlaceholder;
}
- ImageInfos.Add(image);
+ else
+ {
+ var currentCount = ImageInfos.Length;
+ var newList = ImageInfos.ToArray(currentCount + 1);
+ newList[currentCount] = image;
+ ImageInfos = newList;
+ }
}
public void SetImagePath(ImageType type, int index, FileSystemMetadata file)
@@ -1881,7 +1886,10 @@ namespace MediaBrowser.Controller.Entities
if (image == null)
{
- ImageInfos.Add(GetImageInfo(file, type));
+ var currentCount = ImageInfos.Length;
+ var newList = ImageInfos.ToArray(currentCount + 1);
+ newList[currentCount] = GetImageInfo(file, type);
+ ImageInfos = newList;
}
else
{
@@ -1922,7 +1930,12 @@ namespace MediaBrowser.Controller.Entities
public void RemoveImage(ItemImageInfo image)
{
- ImageInfos.Remove(image);
+ RemoveImages(new List<ItemImageInfo> { image });
+ }
+
+ public void RemoveImages(List<ItemImageInfo> deletedImages)
+ {
+ ImageInfos = ImageInfos.Except(deletedImages).ToArray();
}
public virtual Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
@@ -1939,7 +1952,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.IsLocalFile)
.Select(i => FileSystem.GetDirectoryName(i.Path))
.Distinct(StringComparer.OrdinalIgnoreCase)
- .SelectMany(directoryService.GetFilePaths)
+ .SelectMany(i => FileSystem.GetFilePaths(i))
.ToList();
var deletedImages = ImageInfos
@@ -1948,7 +1961,7 @@ namespace MediaBrowser.Controller.Entities
if (deletedImages.Count > 0)
{
- ImageInfos = ImageInfos.Except(deletedImages).ToList();
+ ImageInfos = ImageInfos.Except(deletedImages).ToArray();
}
return deletedImages.Count > 0;
@@ -2068,10 +2081,25 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.IsLocalFile && !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path))
.ToList();
- ImageInfos = ImageInfos.Except(deleted).ToList();
+ if (deleted.Count > 0)
+ {
+ ImageInfos = ImageInfos.Except(deleted).ToArray();
+ }
}
- ImageInfos.AddRange(newImageList.Select(i => GetImageInfo(i, imageType)));
+ if (newImageList.Count > 0)
+ {
+ var currentCount = ImageInfos.Length;
+ var newList = ImageInfos.ToArray(currentCount + newImageList.Count);
+
+ foreach (var image in newImageList)
+ {
+ newList[currentCount] = GetImageInfo(image, imageType);
+ currentCount++;
+ }
+
+ ImageInfos = newList;
+ }
return newImageList.Count > 0;
}
@@ -2112,7 +2140,7 @@ namespace MediaBrowser.Controller.Entities
var extensions = new[] { ".nfo", ".xml", ".srt" }.ToList();
extensions.AddRange(SupportedImageExtensionsList);
- return FileSystem.GetFiles(FileSystem.GetDirectoryName(Path), extensions.ToArray(), false, false)
+ return FileSystem.GetFiles(FileSystem.GetDirectoryName(Path), extensions.ToArray(extensions.Count), false, false)
.Where(i => System.IO.Path.GetFileNameWithoutExtension(i.FullName).StartsWith(filename, StringComparison.OrdinalIgnoreCase))
.ToList();
}
@@ -2272,17 +2300,12 @@ namespace MediaBrowser.Controller.Entities
if (!item.Studios.SequenceEqual(ownedItem.Studios, StringComparer.Ordinal))
{
newOptions.ForceSave = true;
- ownedItem.Studios = item.Studios.ToList();
+ ownedItem.Studios = item.Studios;
}
if (!item.ProductionLocations.SequenceEqual(ownedItem.ProductionLocations, StringComparer.Ordinal))
{
newOptions.ForceSave = true;
- ownedItem.ProductionLocations = item.ProductionLocations.ToList();
- }
- if (!item.Keywords.SequenceEqual(ownedItem.Keywords, StringComparer.Ordinal))
- {
- newOptions.ForceSave = true;
- ownedItem.Keywords = item.Keywords.ToList();
+ ownedItem.ProductionLocations = item.ProductionLocations;
}
if (item.CommunityRating != ownedItem.CommunityRating)
{
@@ -2341,7 +2364,9 @@ namespace MediaBrowser.Controller.Entities
public string GetEtag(User user)
{
- return string.Join("|", GetEtagValues(user).ToArray()).GetMD5().ToString("N");
+ var list = GetEtagValues(user);
+
+ return string.Join("|", list.ToArray(list.Count)).GetMD5().ToString("N");
}
protected virtual List<string> GetEtagValues(User user)
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index d88b7da34..d02e469d4 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -32,15 +32,6 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- protected override bool SupportsShortcutChildren
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
@@ -165,9 +156,9 @@ namespace MediaBrowser.Controller.Entities
public List<string> PhysicalLocationsList { get; set; }
public List<Guid> PhysicalFolderIds { get; set; }
- protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
+ protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
{
- return CreateResolveArgs(directoryService, true).FileSystemChildren;
+ return CreateResolveArgs(directoryService, true).FileSystemChildren.ToArray();
}
private bool _requiresRefresh;
@@ -249,7 +240,7 @@ namespace MediaBrowser.Controller.Entities
var changed = !linkedChildren.SequenceEqual(LinkedChildren, new LinkedChildComparer(FileSystem));
- LinkedChildren = linkedChildren;
+ LinkedChildren = linkedChildren.ToArray(linkedChildren.Count);
var folderIds = PhysicalFolderIds.ToList();
var newFolderIds = physicalFolders.Select(i => i.Id).ToList();
diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs
index 5e792a03a..36855a86c 100644
--- a/MediaBrowser.Controller/Entities/Extensions.cs
+++ b/MediaBrowser.Controller/Entities/Extensions.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Model.Entities;
using System;
using System.Linq;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -12,11 +13,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Adds the trailer URL.
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="url">The URL.</param>
- /// <param name="isDirectLink">if set to <c>true</c> [is direct link].</param>
- /// <exception cref="System.ArgumentNullException">url</exception>
- public static void AddTrailerUrl(this IHasTrailers item, string url, bool isDirectLink)
+ public static void AddTrailerUrl(this IHasTrailers item, string url)
{
if (string.IsNullOrWhiteSpace(url))
{
@@ -27,10 +24,22 @@ namespace MediaBrowser.Controller.Entities
if (current == null)
{
- item.RemoteTrailers.Add(new MediaUrl
+ var mediaUrl = new MediaUrl
{
Url = url
- });
+ };
+
+ if (item.RemoteTrailers.Length == 0)
+ {
+ item.RemoteTrailers = new[] { mediaUrl };
+ }
+ else
+ {
+ var list = item.RemoteTrailers.ToArray(item.RemoteTrailers.Length + 1);
+ list[list.Length - 1] = mediaUrl;
+
+ item.RemoteTrailers = list;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 5d74cf218..46ae9230b 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -20,6 +20,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -37,14 +38,14 @@ namespace MediaBrowser.Controller.Entities
/// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
public bool IsRoot { get; set; }
- public virtual List<LinkedChild> LinkedChildren { get; set; }
+ public LinkedChild[] LinkedChildren { get; set; }
[IgnoreDataMember]
public DateTime? DateLastMediaAdded { get; set; }
public Folder()
{
- LinkedChildren = new List<LinkedChild>();
+ LinkedChildren = EmptyLinkedChildArray;
}
[IgnoreDataMember]
@@ -706,7 +707,7 @@ namespace MediaBrowser.Controller.Entities
public virtual int GetChildCount(User user)
{
- if (LinkedChildren.Count > 0)
+ if (LinkedChildren.Length > 0)
{
if (!(this is ICollectionFolder))
{
@@ -791,7 +792,7 @@ namespace MediaBrowser.Controller.Entities
query.StartIndex = null;
query.Limit = null;
- var itemsList = LibraryManager.GetItemList(query);
+ IEnumerable<BaseItem> itemsList = LibraryManager.GetItemList(query);
var user = query.User;
if (user != null)
@@ -843,7 +844,7 @@ namespace MediaBrowser.Controller.Entities
private bool RequiresPostFiltering(InternalItemsQuery query)
{
- if (LinkedChildren.Count > 0)
+ if (LinkedChildren.Length > 0)
{
if (!(this is ICollectionFolder))
{
@@ -921,12 +922,6 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (query.AirDays.Length > 0)
- {
- Logger.Debug("Query requires post-filtering due to AirDays");
- return true;
- }
-
if (query.SeriesStatuses.Length > 0)
{
Logger.Debug("Query requires post-filtering due to SeriesStatuses");
@@ -970,6 +965,27 @@ namespace MediaBrowser.Controller.Entities
return GetItemsInternal(query);
}
+ public BaseItem[] GetItemList(InternalItemsQuery query)
+ {
+ query.EnableTotalRecordCount = false;
+
+ if (query.ItemIds.Length > 0)
+ {
+ var result = LibraryManager.GetItemList(query);
+
+ if (query.SortBy.Length == 0)
+ {
+ var ids = query.ItemIds.ToList();
+
+ // Try to preserve order
+ return result.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
+ }
+ return result.ToArray(result.Count);
+ }
+
+ return GetItemsInternal(query).Items;
+ }
+
protected virtual QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
{
if (SourceType == SourceType.Channel)
@@ -1203,7 +1219,7 @@ namespace MediaBrowser.Controller.Entities
return GetLinkedChildren();
}
- if (LinkedChildren.Count == 0)
+ if (LinkedChildren.Length == 0)
{
return new List<BaseItem>();
}
@@ -1292,14 +1308,9 @@ namespace MediaBrowser.Controller.Entities
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
protected virtual bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
{
- var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
- var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
-
- List<LinkedChild> newShortcutLinks;
-
if (SupportsShortcutChildren)
{
- newShortcutLinks = fileSystemChildren
+ var newShortcutLinks = fileSystemChildren
.Where(i => !i.IsDirectory && FileSystem.IsShortcut(i.FullName))
.Select(i =>
{
@@ -1330,16 +1341,17 @@ namespace MediaBrowser.Controller.Entities
})
.Where(i => i != null)
.ToList();
- }
- else { newShortcutLinks = new List<LinkedChild>(); }
- if (!newShortcutLinks.SequenceEqual(currentShortcutLinks, new LinkedChildComparer(FileSystem)))
- {
- Logger.Info("Shortcut links have changed for {0}", Path);
+ var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
- newShortcutLinks.AddRange(currentManualLinks);
- LinkedChildren = newShortcutLinks;
- return true;
+ if (!newShortcutLinks.SequenceEqual(currentShortcutLinks, new LinkedChildComparer(FileSystem)))
+ {
+ Logger.Info("Shortcut links have changed for {0}", Path);
+
+ newShortcutLinks.AddRange(LinkedChildren.Where(i => i.Type == LinkedChildType.Manual));
+ LinkedChildren = newShortcutLinks.ToArray(newShortcutLinks.Count);
+ return true;
+ }
}
foreach (var child in LinkedChildren)
@@ -1370,15 +1382,15 @@ namespace MediaBrowser.Controller.Entities
EnableTotalRecordCount = false
};
- if (!user.Configuration.DisplayMissingEpisodes || !user.Configuration.DisplayUnairedEpisodes)
+ if (!user.Configuration.DisplayMissingEpisodes)
{
query.IsVirtualItem = false;
}
- var itemsResult = GetItems(query);
+ var itemsResult = GetItemList(query);
// Sweep through recursively and update status
- var tasks = itemsResult.Items.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
+ var tasks = itemsResult.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
@@ -1390,7 +1402,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns>
public override async Task MarkUnplayed(User user)
{
- var itemsResult = GetItems(new InternalItemsQuery
+ var itemsResult = GetItemList(new InternalItemsQuery
{
User = user,
Recursive = true,
@@ -1400,14 +1412,14 @@ namespace MediaBrowser.Controller.Entities
});
// Sweep through recursively and update status
- var tasks = itemsResult.Items.Select(c => c.MarkUnplayed(user));
+ var tasks = itemsResult.Select(c => c.MarkUnplayed(user));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
public override bool IsPlayed(User user)
{
- var itemsResult = GetItems(new InternalItemsQuery(user)
+ var itemsResult = GetItemList(new InternalItemsQuery(user)
{
Recursive = true,
IsFolder = false,
@@ -1416,7 +1428,7 @@ namespace MediaBrowser.Controller.Entities
});
- return itemsResult.Items
+ return itemsResult
.All(i => i.IsPlayed(user));
}
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index baefc9dfa..eb2638ee4 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -13,14 +13,14 @@ namespace MediaBrowser.Controller.Entities
{
public Game()
{
- MultiPartGameFiles = new List<string>();
- RemoteTrailers = new List<MediaUrl>();
- LocalTrailerIds = new List<Guid>();
- RemoteTrailerIds = new List<Guid>();
+ MultiPartGameFiles = EmptyStringArray;
+ RemoteTrailers = EmptyMediaUrlArray;
+ LocalTrailerIds = EmptyGuidArray;
+ RemoteTrailerIds = EmptyGuidArray;
}
- public List<Guid> LocalTrailerIds { get; set; }
- public List<Guid> RemoteTrailerIds { get; set; }
+ public Guid[] LocalTrailerIds { get; set; }
+ public Guid[] RemoteTrailerIds { get; set; }
public override bool CanDownload()
{
@@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets the type of the media.
@@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Holds the paths to the game files in the event this is a multipart game
/// </summary>
- public List<string> MultiPartGameFiles { get; set; }
+ public string[] MultiPartGameFiles { get; set; }
public override List<string> GetUserDataKeys()
{
@@ -100,7 +100,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<FileSystemMetadata> GetDeletePaths()
{
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
return new[] {
new FileSystemMetadata
@@ -127,16 +127,5 @@ namespace MediaBrowser.Controller.Entities
return id;
}
-
- /// <summary>
- /// Gets the trailer ids.
- /// </summary>
- /// <returns>List&lt;Guid&gt;.</returns>
- public List<Guid> GetTrailerIds()
- {
- var list = LocalTrailerIds.ToList();
- list.AddRange(RemoteTrailerIds);
- return list;
- }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasId.cs b/MediaBrowser.Controller/Entities/IHasId.cs
deleted file mode 100644
index 9698adf7a..000000000
--- a/MediaBrowser.Controller/Entities/IHasId.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasId
- {
- Guid Id { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
deleted file mode 100644
index e2b3c0777..000000000
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasImages : IHasProviderIds, IHasId
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; set; }
-
- /// <summary>
- /// Gets the path.
- /// </summary>
- /// <value>The path.</value>
- string Path { get; set; }
-
- /// <summary>
- /// Gets the file name without extension.
- /// </summary>
- /// <value>The file name without extension.</value>
- string FileNameWithoutExtension { get; }
-
- /// <summary>
- /// Gets the type of the location.
- /// </summary>
- /// <value>The type of the location.</value>
- LocationType LocationType { get; }
-
- /// <summary>
- /// Gets the locked fields.
- /// </summary>
- /// <value>The locked fields.</value>
- List<MetadataFields> LockedFields { get; }
-
- /// <summary>
- /// Gets the images.
- /// </summary>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>IEnumerable{ItemImageInfo}.</returns>
- IEnumerable<ItemImageInfo> GetImages(ImageType imageType);
-
- /// <summary>
- /// Gets the image path.
- /// </summary>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>System.String.</returns>
- string GetImagePath(ImageType imageType, int imageIndex);
-
- /// <summary>
- /// Gets the image information.
- /// </summary>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>ItemImageInfo.</returns>
- ItemImageInfo GetImageInfo(ImageType imageType, int imageIndex);
-
- /// <summary>
- /// Sets the image.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="index">The index.</param>
- /// <param name="file">The file.</param>
- void SetImagePath(ImageType type, int index, FileSystemMetadata file);
-
- /// <summary>
- /// Determines whether the specified type has image.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns><c>true</c> if the specified type has image; otherwise, <c>false</c>.</returns>
- bool HasImage(ImageType type, int imageIndex);
-
- /// <summary>
- /// Allowses the multiple images.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool AllowsMultipleImages(ImageType type);
-
- /// <summary>
- /// Swaps the images.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="index1">The index1.</param>
- /// <param name="index2">The index2.</param>
- /// <returns>Task.</returns>
- Task SwapImages(ImageType type, int index1, int index2);
-
- /// <summary>
- /// Gets the display type of the media.
- /// </summary>
- /// <value>The display type of the media.</value>
- string DisplayMediaType { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image path.
- /// </summary>
- /// <value>The primary image path.</value>
- string PrimaryImagePath { get; }
-
- /// <summary>
- /// Gets the preferred metadata language.
- /// </summary>
- /// <returns>System.String.</returns>
- string GetPreferredMetadataLanguage();
-
- /// <summary>
- /// Validates the images and returns true or false indicating if any were removed.
- /// </summary>
- bool ValidateImages(IDirectoryService directoryService);
-
- /// <summary>
- /// Gets a value indicating whether this instance is owned item.
- /// </summary>
- /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
- bool IsOwnedItem { get; }
-
- /// <summary>
- /// Gets the containing folder path.
- /// </summary>
- /// <value>The containing folder path.</value>
- string ContainingFolderPath { get; }
-
- /// <summary>
- /// Adds the images.
- /// </summary>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="images">The images.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool AddImages(ImageType imageType, List<FileSystemMetadata> images);
-
- /// <summary>
- /// Determines whether [is save local metadata enabled].
- /// </summary>
- /// <returns><c>true</c> if [is save local metadata enabled]; otherwise, <c>false</c>.</returns>
- bool IsSaveLocalMetadataEnabled();
-
- /// <summary>
- /// Gets a value indicating whether [supports local metadata].
- /// </summary>
- /// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value>
- bool SupportsLocalMetadata { get; }
-
- bool DetectIsInMixedFolder();
-
- /// <summary>
- /// Gets a value indicating whether this instance is locked.
- /// </summary>
- /// <value><c>true</c> if this instance is locked; otherwise, <c>false</c>.</value>
- bool IsLocked { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports remote image downloading].
- /// </summary>
- /// <value><c>true</c> if [supports remote image downloading]; otherwise, <c>false</c>.</value>
- bool SupportsRemoteImageDownloading { get; }
-
- /// <summary>
- /// Gets the internal metadata path.
- /// </summary>
- /// <returns>System.String.</returns>
- string GetInternalMetadataPath();
-
- /// <summary>
- /// Gets a value indicating whether [always scan internal metadata path].
- /// </summary>
- /// <value><c>true</c> if [always scan internal metadata path]; otherwise, <c>false</c>.</value>
- bool AlwaysScanInternalMetadataPath { get; }
-
- /// <summary>
- /// Determines whether [is internet metadata enabled].
- /// </summary>
- /// <returns><c>true</c> if [is internet metadata enabled]; otherwise, <c>false</c>.</returns>
- bool IsInternetMetadataEnabled();
-
- /// <summary>
- /// Removes the image.
- /// </summary>
- /// <param name="image">The image.</param>
- void RemoveImage(ItemImageInfo image);
-
- /// <summary>
- /// Updates to repository.
- /// </summary>
- /// <param name="updateReason">The update reason.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sets the image.
- /// </summary>
- /// <param name="image">The image.</param>
- /// <param name="index">The index.</param>
- void SetImage(ItemImageInfo image, int index);
-
- double? GetDefaultPrimaryImageAspectRatio();
-
- int? ProductionYear { get; set; }
- }
-
- public static class HasImagesExtensions
- {
- /// <summary>
- /// Gets the image path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>System.String.</returns>
- public static string GetImagePath(this IHasImages item, ImageType imageType)
- {
- return item.GetImagePath(imageType, 0);
- }
-
- public static bool HasImage(this IHasImages item, ImageType imageType)
- {
- return item.HasImage(imageType, 0);
- }
-
- /// <summary>
- /// Sets the image path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="file">The file.</param>
- public static void SetImagePath(this IHasImages item, ImageType imageType, FileSystemMetadata file)
- {
- item.SetImagePath(imageType, 0, file);
- }
-
- /// <summary>
- /// Sets the image path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="file">The file.</param>
- public static void SetImagePath(this IHasImages item, ImageType imageType, string file)
- {
- if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = file,
- Type = imageType
- }, 0);
- }
- else
- {
- item.SetImagePath(imageType, BaseItem.FileSystem.GetFileInfo(file));
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
index 832b9f6c1..bf4acdfbd 100644
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Dto;
using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Entities
{
@@ -10,6 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
- IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
+ List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
+ List<MediaStream> GetMediaStreams();
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 62c67336c..59d9bd9f9 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasMetadata
/// </summary>
- public interface IHasMetadata : IHasImages
+ public interface IHasMetadata : IHasProviderIds, IHasUserData
{
/// <summary>
/// Gets the preferred metadata country code.
@@ -64,6 +70,253 @@ namespace MediaBrowser.Controller.Entities
int? GetInheritedParentalRatingValue();
int InheritedParentalRatingValue { get; set; }
List<string> GetInheritedTags();
- List<string> InheritedTags { get; set; }
+ long? RunTimeTicks { get; set; }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ string Name { get; set; }
+
+ /// <summary>
+ /// Gets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ string Path { get; set; }
+
+ /// <summary>
+ /// Gets the file name without extension.
+ /// </summary>
+ /// <value>The file name without extension.</value>
+ string FileNameWithoutExtension { get; }
+
+ /// <summary>
+ /// Gets the type of the location.
+ /// </summary>
+ /// <value>The type of the location.</value>
+ LocationType LocationType { get; }
+
+ /// <summary>
+ /// Gets the locked fields.
+ /// </summary>
+ /// <value>The locked fields.</value>
+ MetadataFields[] LockedFields { get; }
+
+ /// <summary>
+ /// Gets the images.
+ /// </summary>
+ /// <param name="imageType">Type of the image.</param>
+ /// <returns>IEnumerable{ItemImageInfo}.</returns>
+ IEnumerable<ItemImageInfo> GetImages(ImageType imageType);
+
+ /// <summary>
+ /// Gets the image path.
+ /// </summary>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="imageIndex">Index of the image.</param>
+ /// <returns>System.String.</returns>
+ string GetImagePath(ImageType imageType, int imageIndex);
+
+ /// <summary>
+ /// Gets the image information.
+ /// </summary>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="imageIndex">Index of the image.</param>
+ /// <returns>ItemImageInfo.</returns>
+ ItemImageInfo GetImageInfo(ImageType imageType, int imageIndex);
+
+ /// <summary>
+ /// Sets the image.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="index">The index.</param>
+ /// <param name="file">The file.</param>
+ void SetImagePath(ImageType type, int index, FileSystemMetadata file);
+
+ /// <summary>
+ /// Determines whether the specified type has image.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="imageIndex">Index of the image.</param>
+ /// <returns><c>true</c> if the specified type has image; otherwise, <c>false</c>.</returns>
+ bool HasImage(ImageType type, int imageIndex);
+
+ /// <summary>
+ /// Allowses the multiple images.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool AllowsMultipleImages(ImageType type);
+
+ /// <summary>
+ /// Swaps the images.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="index1">The index1.</param>
+ /// <param name="index2">The index2.</param>
+ /// <returns>Task.</returns>
+ Task SwapImages(ImageType type, int index1, int index2);
+
+ /// <summary>
+ /// Gets or sets the primary image path.
+ /// </summary>
+ /// <value>The primary image path.</value>
+ string PrimaryImagePath { get; }
+
+ /// <summary>
+ /// Gets the preferred metadata language.
+ /// </summary>
+ /// <returns>System.String.</returns>
+ string GetPreferredMetadataLanguage();
+
+ /// <summary>
+ /// Validates the images and returns true or false indicating if any were removed.
+ /// </summary>
+ bool ValidateImages(IDirectoryService directoryService);
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ bool IsOwnedItem { get; }
+
+ /// <summary>
+ /// Gets the containing folder path.
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ string ContainingFolderPath { get; }
+
+ /// <summary>
+ /// Adds the images.
+ /// </summary>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="images">The images.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool AddImages(ImageType imageType, List<FileSystemMetadata> images);
+
+ /// <summary>
+ /// Determines whether [is save local metadata enabled].
+ /// </summary>
+ /// <returns><c>true</c> if [is save local metadata enabled]; otherwise, <c>false</c>.</returns>
+ bool IsSaveLocalMetadataEnabled();
+
+ /// <summary>
+ /// Gets a value indicating whether [supports local metadata].
+ /// </summary>
+ /// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value>
+ bool SupportsLocalMetadata { get; }
+
+ bool IsInMixedFolder { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is locked.
+ /// </summary>
+ /// <value><c>true</c> if this instance is locked; otherwise, <c>false</c>.</value>
+ bool IsLocked { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether [supports remote image downloading].
+ /// </summary>
+ /// <value><c>true</c> if [supports remote image downloading]; otherwise, <c>false</c>.</value>
+ bool SupportsRemoteImageDownloading { get; }
+
+ /// <summary>
+ /// Gets the internal metadata path.
+ /// </summary>
+ /// <returns>System.String.</returns>
+ string GetInternalMetadataPath();
+
+ /// <summary>
+ /// Gets a value indicating whether [always scan internal metadata path].
+ /// </summary>
+ /// <value><c>true</c> if [always scan internal metadata path]; otherwise, <c>false</c>.</value>
+ bool AlwaysScanInternalMetadataPath { get; }
+
+ /// <summary>
+ /// Determines whether [is internet metadata enabled].
+ /// </summary>
+ /// <returns><c>true</c> if [is internet metadata enabled]; otherwise, <c>false</c>.</returns>
+ bool IsInternetMetadataEnabled();
+
+ /// <summary>
+ /// Removes the image.
+ /// </summary>
+ /// <param name="image">The image.</param>
+ void RemoveImage(ItemImageInfo image);
+
+ void RemoveImages(List<ItemImageInfo> images);
+
+ /// <summary>
+ /// Updates to repository.
+ /// </summary>
+ /// <param name="updateReason">The update reason.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sets the image.
+ /// </summary>
+ /// <param name="image">The image.</param>
+ /// <param name="index">The index.</param>
+ void SetImage(ItemImageInfo image, int index);
+
+ double? GetDefaultPrimaryImageAspectRatio();
+
+ int? ProductionYear { get; set; }
+
+ string[] Tags { get; set; }
+ }
+
+ public static class HasMetadataExtensions
+ {
+ /// <summary>
+ /// Gets the image path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="imageType">Type of the image.</param>
+ /// <returns>System.String.</returns>
+ public static string GetImagePath(this IHasMetadata item, ImageType imageType)
+ {
+ return item.GetImagePath(imageType, 0);
+ }
+
+ public static bool HasImage(this IHasMetadata item, ImageType imageType)
+ {
+ return item.HasImage(imageType, 0);
+ }
+
+ /// <summary>
+ /// Sets the image path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="file">The file.</param>
+ public static void SetImagePath(this IHasMetadata item, ImageType imageType, FileSystemMetadata file)
+ {
+ item.SetImagePath(imageType, 0, file);
+ }
+
+ /// <summary>
+ /// Sets the image path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="file">The file.</param>
+ public static void SetImagePath(this IHasMetadata item, ImageType imageType, string file)
+ {
+ if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
+ {
+ item.SetImage(new ItemImageInfo
+ {
+ Path = file,
+ Type = imageType
+ }, 0);
+ }
+ else
+ {
+ item.SetImagePath(imageType, BaseItem.FileSystem.GetFileInfo(file));
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
index b3a0dc237..f4905b7dc 100644
--- a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
+++ b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@@ -9,6 +8,6 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the special feature ids.
/// </summary>
/// <value>The special feature ids.</value>
- List<Guid> SpecialFeatureIds { get; set; }
+ Guid[] SpecialFeatureIds { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs
index e5cbdff72..8686c802a 100644
--- a/MediaBrowser.Controller/Entities/IHasTrailers.cs
+++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs
@@ -11,14 +11,14 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
- List<MediaUrl> RemoteTrailers { get; set; }
+ MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the local trailer ids.
/// </summary>
/// <value>The local trailer ids.</value>
- List<Guid> LocalTrailerIds { get; set; }
- List<Guid> RemoteTrailerIds { get; set; }
+ Guid[] LocalTrailerIds { get; set; }
+ Guid[] RemoteTrailerIds { get; set; }
}
public static class HasTrailerExtensions
diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs
index 0029947ee..ce4a482ba 100644
--- a/MediaBrowser.Controller/Entities/IHasUserData.cs
+++ b/MediaBrowser.Controller/Entities/IHasUserData.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
@@ -8,7 +9,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Interface IHasUserData
/// </summary>
- public interface IHasUserData : IHasId
+ public interface IHasUserData
{
List<string> GetUserDataKeys();
@@ -20,5 +21,7 @@ namespace MediaBrowser.Controller.Entities
bool EnableRememberingTrackSelections { get; }
bool SupportsPlayedStatus { get; }
+
+ Guid Id { get; }
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 608e3f56c..04833d049 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -38,12 +38,10 @@ namespace MediaBrowser.Controller.Entities
public string[] ExcludeTags { get; set; }
public string[] ExcludeInheritedTags { get; set; }
public string[] Genres { get; set; }
- public string[] Keywords { get; set; }
public bool? IsSpecialSeason { get; set; }
public bool? IsMissing { get; set; }
public bool? IsUnaired { get; set; }
- public bool? IsVirtualUnaired { get; set; }
public bool? CollapseBoxSetItems { get; set; }
public string NameStartsWithOrGreater { get; set; }
@@ -150,7 +148,6 @@ namespace MediaBrowser.Controller.Entities
public TrailerType[] TrailerTypes { get; set; }
public SourceType[] SourceTypes { get; set; }
- public DayOfWeek[] AirDays { get; set; }
public SeriesStatus[] SeriesStatuses { get; set; }
public string ExternalSeriesId { get; set; }
public string ExternalId { get; set; }
@@ -162,6 +159,7 @@ namespace MediaBrowser.Controller.Entities
public string SeriesPresentationUniqueKey { get; set; }
public bool GroupByPresentationUniqueKey { get; set; }
+ public bool GroupBySeriesPresentationUniqueKey { get; set; }
public bool EnableTotalRecordCount { get; set; }
public bool ForceDirect { get; set; }
public Dictionary<string, string> ExcludeProviderIds { get; set; }
@@ -194,7 +192,6 @@ namespace MediaBrowser.Controller.Entities
OfficialRatings = new string[] { };
SortBy = new string[] { };
MediaTypes = new string[] { };
- Keywords = new string[] { };
IncludeItemTypes = new string[] { };
ExcludeItemTypes = new string[] { };
Genres = new string[] { };
@@ -215,7 +212,6 @@ namespace MediaBrowser.Controller.Entities
PresetViews = new string[] { };
TrailerTypes = new TrailerType[] { };
SourceTypes = new SourceType[] { };
- AirDays = new DayOfWeek[] { };
SeriesStatuses = new SeriesStatus[] { };
OrderBy = new List<Tuple<string, SortOrder>>();
}
diff --git a/MediaBrowser.Controller/Entities/KeywordExtensions.cs b/MediaBrowser.Controller/Entities/KeywordExtensions.cs
deleted file mode 100644
index 5c9afdf3d..000000000
--- a/MediaBrowser.Controller/Entities/KeywordExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
- public static class KeywordExtensions
- {
- public static void AddKeyword(this BaseItem item, string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- if (!item.Keywords.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- item.Keywords.Add(name);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 071ed405f..6ba9577d1 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -21,9 +21,9 @@ namespace MediaBrowser.Controller.Entities.Movies
public BoxSet()
{
- RemoteTrailers = new List<MediaUrl>();
- LocalTrailerIds = new List<Guid>();
- RemoteTrailerIds = new List<Guid>();
+ RemoteTrailers = EmptyMediaUrlArray;
+ LocalTrailerIds = EmptyGuidArray;
+ RemoteTrailerIds = EmptyGuidArray;
DisplayOrder = ItemSortBy.PremiereDate;
Shares = new List<Share>();
@@ -47,14 +47,14 @@ namespace MediaBrowser.Controller.Entities.Movies
}
}
- public List<Guid> LocalTrailerIds { get; set; }
- public List<Guid> RemoteTrailerIds { get; set; }
+ public Guid[] LocalTrailerIds { get; set; }
+ public Guid[] RemoteTrailerIds { get; set; }
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the display order.
@@ -116,7 +116,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
if (IsLegacyBoxSet)
{
- return true;
+ return false;
}
return false;
@@ -148,17 +148,6 @@ namespace MediaBrowser.Controller.Entities.Movies
}
/// <summary>
- /// Gets the trailer ids.
- /// </summary>
- /// <returns>List&lt;Guid&gt;.</returns>
- public List<Guid> GetTrailerIds()
- {
- var list = LocalTrailerIds.ToList();
- list.AddRange(RemoteTrailerIds);
- return list;
- }
-
- /// <summary>
/// Updates the official rating based on content and returns true or false indicating if it changed.
/// </summary>
/// <returns></returns>
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 8a5b726e2..3a41709fe 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -19,20 +19,20 @@ namespace MediaBrowser.Controller.Entities.Movies
/// </summary>
public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
{
- public List<Guid> SpecialFeatureIds { get; set; }
+ public Guid[] SpecialFeatureIds { get; set; }
public Movie()
{
- SpecialFeatureIds = new List<Guid>();
- RemoteTrailers = new List<MediaUrl>();
- LocalTrailerIds = new List<Guid>();
- RemoteTrailerIds = new List<Guid>();
+ SpecialFeatureIds = EmptyGuidArray;
+ RemoteTrailers = EmptyMediaUrlArray;
+ LocalTrailerIds = EmptyGuidArray;
+ RemoteTrailerIds = EmptyGuidArray;
}
- public List<Guid> LocalTrailerIds { get; set; }
- public List<Guid> RemoteTrailerIds { get; set; }
+ public Guid[] LocalTrailerIds { get; set; }
+ public Guid[] RemoteTrailerIds { get; set; }
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the name of the TMDB collection.
@@ -55,22 +55,13 @@ namespace MediaBrowser.Controller.Entities.Movies
return value;
}
- [IgnoreDataMember]
- protected override bool SupportsIsInMixedFolderDetection
- {
- get
- {
- return false;
- }
- }
-
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree
- if (LocationType == LocationType.FileSystem && GetParent() != null && !DetectIsInMixedFolder())
+ if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder)
{
var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
@@ -86,7 +77,7 @@ namespace MediaBrowser.Controller.Entities.Movies
private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LibraryManager.FindExtras(this, fileSystemChildren, options.DirectoryService).ToList();
- var newItemIds = newItems.Select(i => i.Id).ToList();
+ var newItemIds = newItems.Select(i => i.Id).ToArray();
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
@@ -108,7 +99,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var info = GetItemLookupInfo<MovieInfo>();
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
var name = System.IO.Path.GetFileName(ContainingFolderPath);
@@ -145,7 +136,7 @@ namespace MediaBrowser.Controller.Entities.Movies
else
{
// Try to get the year from the folder name
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 7344cb8e4..2028c1c3b 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -8,6 +8,7 @@ namespace MediaBrowser.Controller.Entities
{
public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasLookupInfo<MusicVideoInfo>
{
+ [IgnoreDataMember]
public List<string> Artists { get; set; }
public MusicVideo()
@@ -24,15 +25,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- [IgnoreDataMember]
- protected override bool SupportsIsInMixedFolderDetection
- {
- get
- {
- return false;
- }
- }
-
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Music;
@@ -61,7 +53,7 @@ namespace MediaBrowser.Controller.Entities
else
{
// Try to get the year from the folder name
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 45b4de1b3..c30e0ef8e 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -17,14 +17,14 @@ namespace MediaBrowser.Controller.Entities.TV
{
public Episode()
{
- RemoteTrailers = new List<MediaUrl>();
- LocalTrailerIds = new List<Guid>();
- RemoteTrailerIds = new List<Guid>();
+ RemoteTrailers = EmptyMediaUrlArray;
+ LocalTrailerIds = EmptyGuidArray;
+ RemoteTrailerIds = EmptyGuidArray;
}
- public List<Guid> LocalTrailerIds { get; set; }
- public List<Guid> RemoteTrailerIds { get; set; }
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public Guid[] LocalTrailerIds { get; set; }
+ public Guid[] RemoteTrailerIds { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets the season in which it aired.
@@ -282,17 +282,11 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
- return LocationType == LocationType.Virtual && !IsUnaired;
+ return LocationType == LocationType.Virtual;
}
}
[IgnoreDataMember]
- public bool IsVirtualUnaired
- {
- get { return LocationType == LocationType.Virtual && IsUnaired; }
- }
-
- [IgnoreDataMember]
public Guid? SeasonId { get; set; }
[IgnoreDataMember]
public Guid? SeriesId { get; set; }
@@ -346,7 +340,6 @@ namespace MediaBrowser.Controller.Entities.TV
id.IsMissingEpisode = IsMissingEpisode;
id.IndexNumberEnd = IndexNumberEnd;
- id.IsVirtualUnaired = IsVirtualUnaired;
return id;
}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 8b73b80b0..3350a6579 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -22,13 +22,15 @@ namespace MediaBrowser.Controller.Entities.TV
{
public Series()
{
- AirDays = new List<DayOfWeek>();
-
- RemoteTrailers = new List<MediaUrl>();
- LocalTrailerIds = new List<Guid>();
- RemoteTrailerIds = new List<Guid>();
+ RemoteTrailers = EmptyMediaUrlArray;
+ LocalTrailerIds = EmptyGuidArray;
+ RemoteTrailerIds = EmptyGuidArray;
+ AirDays = new DayOfWeek[] { };
}
+ public DayOfWeek[] AirDays { get; set; }
+ public string AirTime { get; set; }
+
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
@@ -62,10 +64,10 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
- public List<Guid> LocalTrailerIds { get; set; }
- public List<Guid> RemoteTrailerIds { get; set; }
+ public Guid[] LocalTrailerIds { get; set; }
+ public Guid[] RemoteTrailerIds { get; set; }
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// airdate, dvd or absolute
@@ -77,16 +79,6 @@ namespace MediaBrowser.Controller.Entities.TV
/// </summary>
/// <value>The status.</value>
public SeriesStatus? Status { get; set; }
- /// <summary>
- /// Gets or sets the air days.
- /// </summary>
- /// <value>The air days.</value>
- public List<DayOfWeek> AirDays { get; set; }
- /// <summary>
- /// Gets or sets the air time.
- /// </summary>
- /// <value>The air time.</value>
- public string AirTime { get; set; }
/// <summary>
/// Gets or sets the date last episode added.
@@ -193,7 +185,7 @@ namespace MediaBrowser.Controller.Entities.TV
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name };
+ query.IncludeItemTypes = new[] { typeof(Episode).Name };
}
query.IsVirtualItem = false;
query.Limit = 0;
@@ -225,17 +217,6 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
- /// <summary>
- /// Gets the trailer ids.
- /// </summary>
- /// <returns>List&lt;Guid&gt;.</returns>
- public List<Guid> GetTrailerIds()
- {
- var list = LocalTrailerIds.ToList();
- list.AddRange(RemoteTrailerIds);
- return list;
- }
-
[IgnoreDataMember]
public bool ContainsEpisodesWithoutSeasonFolders
{
@@ -273,18 +254,10 @@ namespace MediaBrowser.Controller.Entities.TV
query.IncludeItemTypes = new[] { typeof(Season).Name };
query.SortBy = new[] {ItemSortBy.SortName};
- if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
- {
- query.IsVirtualItem = false;
- }
- else if (!config.DisplayMissingEpisodes)
+ if (!config.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
- else if (!config.DisplayUnairedEpisodes)
- {
- query.IsVirtualUnaired = false;
- }
}
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
@@ -332,27 +305,18 @@ namespace MediaBrowser.Controller.Entities.TV
DtoOptions = options
};
var config = user.Configuration;
- if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
- {
- query.IsVirtualItem = false;
- }
- else if (!config.DisplayMissingEpisodes)
+ if (!config.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
- else if (!config.DisplayUnairedEpisodes)
- {
- query.IsVirtualUnaired = false;
- }
- var allItems = LibraryManager.GetItemList(query).ToList();
+ var allItems = LibraryManager.GetItemList(query);
- var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
+ var allSeriesEpisodes = allItems.OfType<Episode>();
var allEpisodes = allItems.OfType<Season>()
.SelectMany(i => i.GetEpisodes(this, user, allSeriesEpisodes, options))
- .Reverse()
- .ToList();
+ .Reverse();
// Specials could appear twice based on above - once in season 0, once in the aired season
// This depends on settings for that series
@@ -365,20 +329,22 @@ namespace MediaBrowser.Controller.Entities.TV
{
// Refresh bottom up, children first, then the boxset
// By then hopefully the movies within will have Tmdb collection values
- var items = GetRecursiveChildren().ToList();
-
- var seasons = items.OfType<Season>().ToList();
- var otherItems = items.Except(seasons).ToList();
+ var items = GetRecursiveChildren();
- var totalItems = seasons.Count + otherItems.Count;
+ var totalItems = items.Count;
var numComplete = 0;
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
// Refresh seasons
- foreach (var item in seasons)
+ foreach (var item in items)
{
+ if (!(item is Season))
+ {
+ continue;
+ }
+
cancellationToken.ThrowIfCancellationRequested();
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -390,8 +356,13 @@ namespace MediaBrowser.Controller.Entities.TV
}
// Refresh episodes and other children
- foreach (var item in otherItems)
+ foreach (var item in items)
{
+ if ((item is Season))
+ {
+ continue;
+ }
+
cancellationToken.ThrowIfCancellationRequested();
var skipItem = false;
@@ -445,18 +416,10 @@ namespace MediaBrowser.Controller.Entities.TV
if (user != null)
{
var config = user.Configuration;
- if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
- {
- query.IsVirtualItem = false;
- }
- else if (!config.DisplayMissingEpisodes)
+ if (!config.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
- else if (!config.DisplayUnairedEpisodes)
- {
- query.IsVirtualUnaired = false;
- }
}
var allItems = LibraryManager.GetItemList(query).OfType<Episode>();
diff --git a/MediaBrowser.Controller/Entities/TagExtensions.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs
index 0e1df72cd..e5d8f35d9 100644
--- a/MediaBrowser.Controller/Entities/TagExtensions.cs
+++ b/MediaBrowser.Controller/Entities/TagExtensions.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -12,9 +14,21 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("name");
}
- if (!item.Tags.Contains(name, StringComparer.OrdinalIgnoreCase))
+ var current = item.Tags;
+
+ if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
{
- item.Tags.Add(name);
+ if (current.Length == 0)
+ {
+ item.Tags = new[] { name };
+ }
+ else
+ {
+ var list = current.ToArray(current.Length + 1);
+ list[list.Length - 1] = name;
+
+ item.Tags = list;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index b4a142a8e..c5144aadf 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.Entities
public Trailer()
{
RemoteTrailers = new List<MediaUrl>();
- Keywords = new List<string>();
TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
}
@@ -48,7 +47,7 @@ namespace MediaBrowser.Controller.Entities
info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer);
- if (!DetectIsInMixedFolder() && LocationType == LocationType.FileSystem)
+ if (!IsInMixedFolder && LocationType == LocationType.FileSystem)
{
info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
}
@@ -74,7 +73,7 @@ namespace MediaBrowser.Controller.Entities
else
{
// Try to get the year from the folder name
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 0d2d69c94..4c44a613b 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -82,7 +82,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
- var result = GetItems(new InternalItemsQuery
+ var result = GetItemList(new InternalItemsQuery
{
User = user,
EnableTotalRecordCount = false,
@@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Entities
});
- return result.Items;
+ return result;
}
public override bool CanDelete()
@@ -105,7 +105,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
{
- var result = GetItems(new InternalItemsQuery
+ var result = GetItemList(new InternalItemsQuery
{
User = user,
Recursive = true,
@@ -117,7 +117,7 @@ namespace MediaBrowser.Controller.Entities
});
- return result.Items.Where(i => UserViewBuilder.FilterItem(i, query));
+ return result.Where(i => UserViewBuilder.FilterItem(i, query));
}
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 91e24caeb..9323404e3 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -779,7 +779,6 @@ namespace MediaBrowser.Controller.Entities
items = FilterVirtualEpisodes(items,
query.IsMissing,
- query.IsVirtualUnaired,
query.IsUnaired);
if (collapseBoxSetItems && user != null)
@@ -1065,7 +1064,6 @@ namespace MediaBrowser.Controller.Entities
private static IEnumerable<BaseItem> FilterVirtualEpisodes(
IEnumerable<BaseItem> items,
bool? isMissing,
- bool? isVirtualUnaired,
bool? isUnaired)
{
if (isMissing.HasValue)
@@ -1096,20 +1094,6 @@ namespace MediaBrowser.Controller.Entities
});
}
- if (isVirtualUnaired.HasValue)
- {
- var val = isVirtualUnaired.Value;
- items = items.Where(i =>
- {
- var e = i as Episode;
- if (e != null)
- {
- return e.IsVirtualUnaired == val;
- }
- return true;
- });
- }
-
return items;
}
@@ -1387,8 +1371,8 @@ namespace MediaBrowser.Controller.Entities
if (movie != null)
{
var ok = filterValue
- ? movie.SpecialFeatureIds.Count > 0
- : movie.SpecialFeatureIds.Count == 0;
+ ? movie.SpecialFeatureIds.Length > 0
+ : movie.SpecialFeatureIds.Length == 0;
if (!ok)
{
@@ -1463,7 +1447,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasThemeSong.Value;
- var themeCount = item.ThemeSongIds.Count;
+ var themeCount = item.ThemeSongIds.Length;
var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok)
@@ -1476,7 +1460,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasThemeVideo.Value;
- var themeCount = item.ThemeVideoIds.Count;
+ var themeCount = item.ThemeVideoIds.Length;
var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok)
@@ -1674,15 +1658,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- if (query.AirDays.Length > 0)
- {
- var ok = new[] { item }.OfType<Series>().Any(p => p.AirDays != null && query.AirDays.Any(d => p.AirDays.Contains(d)));
- if (!ok)
- {
- return false;
- }
- }
-
if (query.SeriesStatuses.Length > 0)
{
var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value));
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index c5d9b9203..fbeefbbd9 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -30,9 +31,9 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public string PrimaryVersionId { get; set; }
- public List<string> AdditionalParts { get; set; }
- public List<string> LocalAlternateVersions { get; set; }
- public List<LinkedChild> LinkedAlternateVersions { get; set; }
+ public string[] AdditionalParts { get; set; }
+ public string[] LocalAlternateVersions { get; set; }
+ public LinkedChild[] LinkedAlternateVersions { get; set; }
[IgnoreDataMember]
public override bool SupportsPlayedStatus
@@ -52,6 +53,12 @@ namespace MediaBrowser.Controller.Entities
}
}
+ /// <summary>
+ /// Gets or sets the display type of the media.
+ /// </summary>
+ /// <value>The display type of the media.</value>
+ public string DisplayMediaType { get; set; }
+
[IgnoreDataMember]
public override bool SupportsPositionTicksResume
{
@@ -77,15 +84,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- [IgnoreDataMember]
- protected override bool SupportsIsInMixedFolderDetection
- {
- get
- {
- return true;
- }
- }
-
public override string CreatePresentationUniqueKey()
{
if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
@@ -121,7 +119,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the subtitle paths.
/// </summary>
/// <value>The subtitle paths.</value>
- public List<string> SubtitleFiles { get; set; }
+ public string[] SubtitleFiles { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has subtitles.
@@ -158,11 +156,6 @@ namespace MediaBrowser.Controller.Entities
public Video3DFormat? Video3DFormat { get; set; }
/// <summary>
- /// If the video is a folder-rip, this will hold the file list for the largest playlist
- /// </summary>
- public List<string> PlayableStreamFileNames { get; set; }
-
- /// <summary>
/// Gets the playable stream files.
/// </summary>
/// <returns>List{System.String}.</returns>
@@ -171,6 +164,11 @@ namespace MediaBrowser.Controller.Entities
return GetPlayableStreamFiles(Path);
}
+ public List<string> GetPlayableStreamFileNames()
+ {
+ return GetPlayableStreamFiles().Select(System.IO.Path.GetFileName).ToList(); ;
+ }
+
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
@@ -179,17 +177,15 @@ namespace MediaBrowser.Controller.Entities
public Video()
{
- PlayableStreamFileNames = new List<string>();
- AdditionalParts = new List<string>();
- LocalAlternateVersions = new List<string>();
- SubtitleFiles = new List<string>();
- LinkedAlternateVersions = new List<LinkedChild>();
+ AdditionalParts = EmptyStringArray;
+ LocalAlternateVersions = EmptyStringArray;
+ SubtitleFiles = EmptyStringArray;
+ LinkedAlternateVersions = EmptyLinkedChildArray;
}
public override bool CanDownload()
{
- if (VideoType == VideoType.HdDvd || VideoType == VideoType.Dvd ||
- VideoType == VideoType.BluRay)
+ if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
{
return false;
}
@@ -218,20 +214,20 @@ namespace MediaBrowser.Controller.Entities
return item.MediaSourceCount;
}
}
- return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
+ return LinkedAlternateVersions.Length + LocalAlternateVersions.Length + 1;
}
}
[IgnoreDataMember]
public bool IsStacked
{
- get { return AdditionalParts.Count > 0; }
+ get { return AdditionalParts.Length > 0; }
}
[IgnoreDataMember]
public bool HasLocalAlternateVersions
{
- get { return LocalAlternateVersions.Count > 0; }
+ get { return LocalAlternateVersions.Length > 0; }
}
public IEnumerable<Guid> GetAdditionalPartIds()
@@ -339,8 +335,7 @@ namespace MediaBrowser.Controller.Entities
if (!IsPlaceHolder)
{
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd ||
- VideoType == VideoType.HdDvd)
+ if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
{
return Path;
}
@@ -357,7 +352,7 @@ namespace MediaBrowser.Controller.Entities
{
if (LocationType == LocationType.FileSystem)
{
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd || VideoType == VideoType.HdDvd)
+ if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
{
return System.IO.Path.GetFileName(Path);
}
@@ -402,13 +397,52 @@ namespace MediaBrowser.Controller.Entities
/// <returns>List{System.String}.</returns>
public List<string> GetPlayableStreamFiles(string rootPath)
{
+ if (VideoType == VideoType.VideoFile)
+ {
+ return new List<string>();
+ }
+
var allFiles = FileSystem.GetFilePaths(rootPath, true).ToList();
- return PlayableStreamFileNames.Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
+ var videoType = VideoType;
+
+ if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.BluRay)
+ {
+ videoType = VideoType.BluRay;
+ }
+ else if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.Dvd)
+ {
+ videoType = VideoType.Dvd;
+ }
+
+ return QueryPlayableStreamFiles(rootPath, videoType).Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
.Where(f => !string.IsNullOrEmpty(f))
.ToList();
}
+ public static List<string> QueryPlayableStreamFiles(string rootPath, VideoType videoType)
+ {
+ if (videoType == VideoType.Dvd)
+ {
+ return FileSystem.GetFiles(rootPath, new[] { ".vob" }, false, true)
+ .OrderByDescending(i => i.Length)
+ .ThenBy(i => i.FullName)
+ .Take(1)
+ .Select(i => i.FullName)
+ .ToList();
+ }
+ if (videoType == VideoType.BluRay)
+ {
+ return FileSystem.GetFiles(rootPath, new[] { ".m2ts" }, false, true)
+ .OrderByDescending(i => i.Length)
+ .ThenBy(i => i.FullName)
+ .Take(1)
+ .Select(i => i.FullName)
+ .ToList();
+ }
+ return new List<string>();
+ }
+
/// <summary>
/// Gets a value indicating whether [is3 D].
/// </summary>
@@ -500,7 +534,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<FileSystemMetadata> GetDeletePaths()
{
- if (!DetectIsInMixedFolder())
+ if (!IsInMixedFolder)
{
return new[] {
new FileSystemMetadata
@@ -514,17 +548,12 @@ namespace MediaBrowser.Controller.Entities
return base.GetDeletePaths();
}
- public IEnumerable<MediaStream> GetMediaStreams()
+ public List<MediaStream> GetMediaStreams()
{
- var mediaSource = GetMediaSources(false)
- .FirstOrDefault();
-
- if (mediaSource == null)
+ return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
{
- return new List<MediaStream>();
- }
-
- return mediaSource.MediaStreams;
+ ItemId = Id
+ });
}
public virtual MediaStream GetDefaultVideoStream()
@@ -572,7 +601,7 @@ namespace MediaBrowser.Controller.Entities
return list;
}
- public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+ public virtual List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
if (SourceType == SourceType.Channel)
{
@@ -619,8 +648,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("media");
}
- var mediaStreams = MediaSourceManager.GetMediaStreams(media.Id)
- .ToList();
+ var mediaStreams = MediaSourceManager.GetMediaStreams(media.Id);
var locationType = media.LocationType;
@@ -639,7 +667,6 @@ namespace MediaBrowser.Controller.Entities
Size = media.Size,
Timestamp = media.Timestamp,
Type = type,
- PlayableStreamFileNames = media.PlayableStreamFileNames.ToList(),
SupportsDirectStream = media.VideoType == VideoType.VideoFile,
IsRemote = media.IsShortcut
};
@@ -714,10 +741,6 @@ namespace MediaBrowser.Controller.Entities
{
terms.Add("DVD");
}
- else if (video.VideoType == VideoType.HdDvd)
- {
- terms.Add("HD-DVD");
- }
else if (video.VideoType == VideoType.Iso)
{
if (video.IsoType.HasValue)
@@ -781,7 +804,7 @@ namespace MediaBrowser.Controller.Entities
}
}
- return string.Join("/", terms.ToArray());
+ return string.Join("/", terms.ToArray(terms.Count));
}
}
diff --git a/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs b/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs
deleted file mode 100644
index 9a5b96a24..000000000
--- a/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.FileOrganization
-{
- public interface IFileOrganizationService
- {
- event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
- event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
- event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
- event EventHandler LogReset;
-
- /// <summary>
- /// Processes the new files.
- /// </summary>
- void BeginProcessNewFiles();
-
- /// <summary>
- /// Deletes the original file.
- /// </summary>
- /// <param name="resultId">The result identifier.</param>
- /// <returns>Task.</returns>
- Task DeleteOriginalFile(string resultId);
-
- /// <summary>
- /// Clears the log.
- /// </summary>
- /// <returns>Task.</returns>
- Task ClearLog();
-
- /// <summary>
- /// Performs the organization.
- /// </summary>
- /// <param name="resultId">The result identifier.</param>
- /// <returns>Task.</returns>
- Task PerformOrganization(string resultId);
-
- /// <summary>
- /// Performs the episode organization.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request);
-
- /// <summary>
- /// Gets the results.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable{FileOrganizationResult}.</returns>
- QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query);
-
- /// <summary>
- /// Gets the result.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>FileOrganizationResult.</returns>
- FileOrganizationResult GetResult(string id);
-
- /// <summary>
- /// Gets the result by source path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>FileOrganizationResult.</returns>
- FileOrganizationResult GetResultBySourcePath(string path);
-
- /// <summary>
- /// Saves the result.
- /// </summary>
- /// <param name="result">The result.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken);
-
- /// <summary>
- /// Returns a list of smart match entries
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable{SmartMatchInfo}.</returns>
- QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query);
-
- /// <summary>
- /// Deletes a smart match entry.
- /// </summary>
- /// <param name="ItemName">Item name.</param>
- /// <param name="matchString">The match string to delete.</param>
- void DeleteSmartMatchEntry(string ItemName, string matchString);
-
- /// <summary>
- /// Attempts to add a an item to the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <param name="fullClientRefresh">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
- /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
- bool AddToInProgressList(FileOrganizationResult result, bool fullClientRefresh);
-
- /// <summary>
- /// Removes an item from the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
- bool RemoveFromInprogressList(FileOrganizationResult result);
- }
-}
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index 97b778d0c..64a7610fe 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -13,6 +13,17 @@ namespace MediaBrowser.Controller.IO
/// </summary>
public static class FileData
{
+ private static Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(FileSystemMetadata[] list)
+ {
+ var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var file in list)
+ {
+ dict[file.FullName] = file;
+ }
+ return dict;
+ }
+
/// <summary>
/// Gets the filtered file system entries.
/// </summary>
@@ -42,13 +53,13 @@ namespace MediaBrowser.Controller.IO
throw new ArgumentNullException("args");
}
+ var entries = directoryService.GetFileSystemEntries(path);
+
if (!resolveShortcuts && flattenFolderDepth == 0)
{
- return directoryService.GetFileSystemDictionary(path);
+ return GetFileSystemDictionary(entries);
}
- var entries = directoryService.GetFileSystemEntries(path);
-
var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in entries)
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index b726c267c..025254d4b 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -185,13 +185,6 @@ namespace MediaBrowser.Controller.Library
SortOrder sortOrder);
/// <summary>
- /// Ensure supplied item has only one instance throughout
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>The proper instance to the item</returns>
- BaseItem GetOrAddByReferenceItem(BaseItem item);
-
- /// <summary>
/// Gets the user root folder.
/// </summary>
/// <returns>UserRootFolder.</returns>
@@ -520,21 +513,21 @@ namespace MediaBrowser.Controller.Library
/// <param name="image">The image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task.</returns>
- Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
+ Task<ItemImageInfo> ConvertImageToLocal(IHasMetadata item, ItemImageInfo image, int imageIndex);
/// <summary>
/// Gets the items.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
- IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
+ List<BaseItem> GetItemList(InternalItemsQuery query);
- IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent);
+ List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent);
/// <summary>
/// Gets the items.
/// </summary>
- IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
+ List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
/// <summary>
/// Gets the items result.
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index 2f8f37789..204033e1d 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -24,19 +24,19 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- IEnumerable<MediaStream> GetMediaStreams(Guid itemId);
+ List<MediaStream> GetMediaStreams(Guid itemId);
/// <summary>
/// Gets the media streams.
/// </summary>
/// <param name="mediaSourceId">The media source identifier.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- IEnumerable<MediaStream> GetMediaStreams(string mediaSourceId);
+ List<MediaStream> GetMediaStreams(string mediaSourceId);
/// <summary>
/// Gets the media streams.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query);
+ List<MediaStream> GetMediaStreams(MediaStreamQuery query);
/// <summary>
/// Gets the playack media sources.
@@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
- IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null);
+ List<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null);
/// <summary>
/// Gets the static media source.
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 95ba671b4..535e6df7e 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -10,16 +10,16 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the instant mix from song.
/// </summary>
- IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions);
-
+ List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions);
+
/// <summary>
/// Gets the instant mix from artist.
/// </summary>
- IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions);
-
+ List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions);
+
/// <summary>
/// Gets the instant mix from genre.
/// </summary>
- IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions);
+ List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions);
}
}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index 3ddda81cd..76b6d8768 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -238,21 +238,6 @@ namespace MediaBrowser.Controller.Library
}
/// <summary>
- /// Determines whether [contains meta file by name] [the specified name].
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns><c>true</c> if [contains meta file by name] [the specified name]; otherwise, <c>false</c>.</returns>
- public bool ContainsMetaFileByName(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException();
- }
-
- return GetFileSystemEntryByName(name) != null;
- }
-
- /// <summary>
/// Determines whether [contains file system entry by name] [the specified name].
/// </summary>
/// <param name="name">The name.</param>
diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs
index 29421ebaf..7c82ec293 100644
--- a/MediaBrowser.Controller/Library/TVUtils.cs
+++ b/MediaBrowser.Controller/Library/TVUtils.cs
@@ -22,13 +22,13 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="day">The day.</param>
/// <returns>List{DayOfWeek}.</returns>
- public static List<DayOfWeek> GetAirDays(string day)
+ public static DayOfWeek[] GetAirDays(string day)
{
if (!string.IsNullOrWhiteSpace(day))
{
if (day.Equals("Daily", StringComparison.OrdinalIgnoreCase))
{
- return new List<DayOfWeek>
+ return new DayOfWeek[]
{
DayOfWeek.Sunday,
DayOfWeek.Monday,
@@ -44,13 +44,13 @@ namespace MediaBrowser.Controller.Library
if (Enum.TryParse(day, true, out value))
{
- return new List<DayOfWeek>
+ return new DayOfWeek[]
{
value
};
}
- return new List<DayOfWeek>();
+ return new DayOfWeek[]{};
}
return null;
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 288b30278..d6855b792 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -284,7 +284,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary>
/// Gets the internal channels.
/// </summary>
- Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken);
+ Task<QueryResult<BaseItem>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken);
/// <summary>
/// Gets the internal recordings.
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index 1bbd1a008..27ff334ee 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv
{
- public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData, IHasStartDate, IHasProgramAttributes
+ public interface ILiveTvRecording : IHasMetadata, IHasMediaSources, IHasUserData, IHasStartDate, IHasProgramAttributes
{
string ServiceName { get; set; }
string ExternalId { get; set; }
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index 9f8234402..25bc10dec 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -151,9 +151,9 @@ namespace MediaBrowser.Controller.LiveTv
return user.Policy.EnableLiveTvManagement;
}
- public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+ public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
- var list = base.GetMediaSources(enablePathSubstitution).ToList();
+ var list = base.GetMediaSources(enablePathSubstitution);
foreach (var mediaSource in list)
{
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 898ea9ff4..4a93d0399 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -122,7 +122,7 @@ namespace MediaBrowser.Controller.LiveTv
return new List<BaseItem>();
}
- public IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+ public List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = new List<MediaSourceInfo>();
@@ -144,6 +144,11 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
+ public List<MediaStream> GetMediaStreams()
+ {
+ return new List<MediaStream>();
+ }
+
protected override string GetInternalMetadataPath(string basePath)
{
return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"), "metadata");
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 9f55a9ff2..3efbc41f1 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -48,22 +48,19 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
+ private static string EmbyServiceName = "Emby";
public override double? GetDefaultPrimaryImageAspectRatio()
{
- if (IsMovie)
+ var serviceName = ServiceName;
+ if (!IsMovie && !string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrWhiteSpace(serviceName))
{
- double value = 2;
- value /= 3;
-
- return value;
+ return null;
}
- else
- {
- double value = 2;
- value /= 3;
- return value;
- }
+ double value = 2;
+ value /= 3;
+
+ return value;
}
[IgnoreDataMember]
@@ -227,7 +224,7 @@ namespace MediaBrowser.Controller.LiveTv
public LiveTvProgramLookupInfo GetLookupInfo()
{
var info = GetItemLookupInfo<LiveTvProgramLookupInfo>();
- info.IsMovie = IsMovie;
+ info.IsMovie = IsMovie;
return info;
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index c29d3dc47..2c26d3556 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -54,15 +54,6 @@ namespace MediaBrowser.Controller.LiveTv
}
[IgnoreDataMember]
- protected override bool SupportsIsInMixedFolderDetection
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
@@ -159,9 +150,9 @@ namespace MediaBrowser.Controller.LiveTv
return user.Policy.EnableLiveTvManagement;
}
- public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+ public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
- var list = base.GetMediaSources(enablePathSubstitution).ToList();
+ var list = base.GetMediaSources(enablePathSubstitution);
foreach (var mediaSource in list)
{
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 22f94695d..26766f51a 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -99,9 +99,6 @@
<Compile Include="Entities\GameSystem.cs" />
<Compile Include="Entities\IHasAspectRatio.cs" />
<Compile Include="Entities\IHasDisplayOrder.cs" />
- <Compile Include="Entities\IHasId.cs" />
- <Compile Include="Entities\IHasImages.cs" />
- <Compile Include="Entities\KeywordExtensions.cs" />
<Compile Include="Entities\IHasMediaSources.cs" />
<Compile Include="Entities\IHasProgramAttributes.cs" />
<Compile Include="Entities\IHasScreenshots.cs" />
@@ -129,7 +126,6 @@
<Compile Include="Entities\UserView.cs" />
<Compile Include="Entities\UserViewBuilder.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
- <Compile Include="FileOrganization\IFileOrganizationService.cs" />
<Compile Include="IO\StreamHelper.cs" />
<Compile Include="Library\DeleteOptions.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" />
@@ -170,7 +166,6 @@
<Compile Include="LiveTv\TimerEventInfo.cs" />
<Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="LiveTv\TunerChannelMapping.cs" />
- <Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" />
<Compile Include="MediaEncoding\EncodingHelper.cs" />
<Compile Include="MediaEncoding\EncodingJobInfo.cs" />
<Compile Include="MediaEncoding\EncodingJobOptions.cs" />
@@ -178,7 +173,6 @@
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
<Compile Include="MediaEncoding\IMediaEncoder.cs" />
<Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
- <Compile Include="MediaEncoding\JobLogger.cs" />
<Compile Include="MediaEncoding\MediaInfoRequest.cs" />
<Compile Include="MediaEncoding\MediaStreamSelector.cs" />
<Compile Include="Net\AuthenticatedAttribute.cs" />
@@ -206,7 +200,6 @@
<Compile Include="Notifications\INotificationTypeFactory.cs" />
<Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
<Compile Include="Notifications\UserNotification.cs" />
- <Compile Include="Persistence\IFileOrganizationRepository.cs" />
<Compile Include="Persistence\MediaStreamQuery.cs" />
<Compile Include="Playlists\IPlaylistManager.cs" />
<Compile Include="Playlists\Playlist.cs" />
@@ -342,7 +335,6 @@
<Compile Include="Sync\ISyncDataProvider.cs" />
<Compile Include="Sync\ISyncManager.cs" />
<Compile Include="Sync\ISyncProvider.cs" />
- <Compile Include="Sync\ISyncRepository.cs" />
<Compile Include="Sync\SyncedFileInfo.cs" />
<Compile Include="Sync\SyncedItemProgress.cs" />
<Compile Include="TV\ITVSeriesManager.cs" />
diff --git a/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs b/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs
deleted file mode 100644
index e11bd6cdf..000000000
--- a/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class ChapterImageRefreshOptions
- {
- public Video Video { get; set; }
-
- public List<ChapterInfo> Chapters { get; set; }
-
- public bool SaveChapters { get; set; }
-
- public bool ExtractImages { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 44bdafc5b..823c893ea 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -4,13 +4,14 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -19,14 +20,12 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IMediaEncoder _mediaEncoder;
- private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly ISubtitleEncoder _subtitleEncoder;
- public EncodingHelper(IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder)
+ public EncodingHelper(IMediaEncoder mediaEncoder, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder)
{
_mediaEncoder = mediaEncoder;
- _config = config;
_fileSystem = fileSystem;
_subtitleEncoder = subtitleEncoder;
}
@@ -151,10 +150,13 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputFormat(string container)
{
- if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrWhiteSpace(container))
{
- return "matroska";
+ return null;
}
+
+ container = container.Replace("mkv", "matroska", StringComparison.OrdinalIgnoreCase);
+
if (string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
{
return "mpegts";
@@ -205,6 +207,14 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return null;
}
+ if (string.Equals(container, "rmvb", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+ if (string.Equals(container, "rtp", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
// Seeing reported failures here, not sure yet if this is related to specfying input format
if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase))
@@ -919,19 +929,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- // Video bitrate must fall within requested value
- if (request.AudioBitRate.HasValue)
- {
- if (!audioStream.BitRate.HasValue || audioStream.BitRate.Value <= 0)
- {
- return false;
- }
- if (audioStream.BitRate.Value > request.AudioBitRate.Value)
- {
- return false;
- }
- }
-
// Channels must fall within requested value
var channels = request.AudioChannels ?? request.MaxAudioChannels;
if (channels.HasValue)
@@ -959,6 +956,19 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ // Video bitrate must fall within requested value
+ if (request.AudioBitRate.HasValue)
+ {
+ if (!audioStream.BitRate.HasValue || audioStream.BitRate.Value <= 0)
+ {
+ return false;
+ }
+ if (audioStream.BitRate.Value > request.AudioBitRate.Value)
+ {
+ return false;
+ }
+ }
+
return request.EnableAutoStreamCopy;
}
@@ -1616,26 +1626,6 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier += " -f " + inputFormat;
}
}
-
- // Only do this for video files due to sometimes unpredictable codec names coming from BDInfo
- if (state.VideoType == VideoType.VideoFile && state.RunTimeTicks.HasValue && string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType))
- {
- foreach (var stream in state.MediaSource.MediaStreams)
- {
- if (!stream.IsExternal && stream.Type != MediaStreamType.Subtitle)
- {
- if (!string.IsNullOrWhiteSpace(stream.Codec) && stream.Index != -1)
- {
- var decoder = GetDecoderFromCodec(stream.Codec);
-
- if (!string.IsNullOrWhiteSpace(decoder))
- {
- inputModifier += " -codec:" + stream.Index.ToString(_usCulture) + " " + decoder;
- }
- }
- }
- }
- }
}
if (state.MediaSource.RequiresLooping)
@@ -1666,14 +1656,33 @@ namespace MediaBrowser.Controller.MediaEncoding
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
+ state.IsoType = mediaSource.IsoType;
+
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
- }
- state.IsoType = mediaSource.IsoType;
-
- state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
+ if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
+ {
+ state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, mediaSource.VideoType.Value);
+ }
+ else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.BluRay)
+ {
+ state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.BluRay);
+ }
+ else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.Dvd)
+ {
+ state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.Dvd);
+ }
+ else
+ {
+ state.PlayableStreamFileNames = new List<string>();
+ }
+ }
+ else
+ {
+ state.PlayableStreamFileNames = new List<string>();
+ }
if (mediaSource.Timestamp.HasValue)
{
@@ -1693,7 +1702,8 @@ namespace MediaBrowser.Controller.MediaEncoding
state.InputAudioSync = "1";
}
- if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(mediaSource.Container, "asf", StringComparison.OrdinalIgnoreCase))
{
// Seeing some stuttering when transcoding wma to audio-only HLS
state.InputAudioSync = "1";
@@ -1767,50 +1777,54 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
+ return GetVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
+ }
+
+ public string GetVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
+ {
// Only use alternative encoders for video files.
// When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
// Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
- if (state.VideoType != VideoType.VideoFile)
+ if (videoType != VideoType.VideoFile)
{
return null;
}
- if (state.VideoStream != null &&
- !string.IsNullOrWhiteSpace(state.VideoStream.Codec) &&
- !string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType) &&
- encodingOptions.EnableHardwareDecoding)
+ if (videoStream != null &&
+ !string.IsNullOrWhiteSpace(videoStream.Codec) &&
+ !string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType))
{
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
- switch (state.MediaSource.VideoStream.Codec.ToLower())
+ switch (videoStream.Codec.ToLower())
{
case "avc":
case "h264":
- if (_mediaEncoder.SupportsDecoder("h264_qsv"))
+ if (_mediaEncoder.SupportsDecoder("h264_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
{
// qsv decoder does not support 10-bit input
- if ((state.VideoStream.BitDepth ?? 8) > 8)
+ if ((videoStream.BitDepth ?? 8) > 8)
{
return null;
}
return "-c:v h264_qsv ";
}
break;
- //case "hevc":
- //case "h265":
- // if (_mediaEncoder.SupportsDecoder("hevc_qsv"))
- // {
- // return "-c:v hevc_qsv ";
- // }
- // break;
+ case "hevc":
+ case "h265":
+ if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
+ {
+ return "-c:v hevc_qsv ";
+ }
+ break;
case "mpeg2video":
- if (_mediaEncoder.SupportsDecoder("mpeg2_qsv"))
+ if (_mediaEncoder.SupportsDecoder("mpeg2_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
{
return "-c:v mpeg2_qsv ";
}
break;
case "vc1":
- if (_mediaEncoder.SupportsDecoder("vc1_qsv"))
+ if (_mediaEncoder.SupportsDecoder("vc1_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
{
return "-c:v vc1_qsv ";
}
@@ -1820,22 +1834,40 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
{
- switch (state.MediaSource.VideoStream.Codec.ToLower())
+ switch (videoStream.Codec.ToLower())
{
case "avc":
case "h264":
- if (_mediaEncoder.SupportsDecoder("h264_cuvid"))
+ if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
{
return "-c:v h264_cuvid ";
}
break;
case "hevc":
case "h265":
- if (_mediaEncoder.SupportsDecoder("hevc_cuvid"))
+ if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
{
return "-c:v hevc_cuvid ";
}
break;
+ case "mpeg2video":
+ if (_mediaEncoder.SupportsDecoder("mpeg2_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
+ {
+ return "-c:v mpeg2_cuvid ";
+ }
+ break;
+ case "vc1":
+ if (_mediaEncoder.SupportsDecoder("vc1_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
+ {
+ return "-c:v vc1_cuvid ";
+ }
+ break;
+ case "mpeg4":
+ if (_mediaEncoder.SupportsDecoder("mpeg4_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase))
+ {
+ return "-c:v mpeg4_cuvid ";
+ }
+ break;
}
}
}
@@ -2100,6 +2132,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var vn = string.Empty;
var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath);
+ hasArt = false;
if (hasArt)
{
diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
index 8683a6af4..81269fe3f 100644
--- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
@@ -1,5 +1,8 @@
-using System.Threading;
+using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -8,9 +11,6 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Refreshes the chapter images.
/// </summary>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{System.Boolean}.</returns>
- Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken);
+ Task<bool> RefreshChapterImages(Video video, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 10d7b9a7e..05bb35771 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -39,29 +39,16 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Extracts the video image.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="threedFormat">The threed format.</param>
- /// <param name="offset">The offset.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary>
/// Extracts the video images on interval.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="threedFormat">The threed format.</param>
- /// <param name="interval">The interval.</param>
- /// <param name="targetDirectory">The target directory.</param>
- /// <param name="filenamePrefix">The filename prefix.</param>
- /// <param name="maxWidth">The maximum width.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
Task ExtractVideoImagesOnInterval(string[] inputFiles,
+ string container,
+ MediaStream videoStream,
MediaProtocol protocol,
Video3DFormat? threedFormat,
TimeSpan interval,
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
deleted file mode 100644
index 03e4f7771..000000000
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class JobLogger
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly ILogger _logger;
-
- public JobLogger(ILogger logger)
- {
- _logger = logger;
- }
-
- public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
- {
- try
- {
- using (var reader = new StreamReader(source))
- {
- while (!reader.EndOfStream)
- {
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
-
- ParseLogLine(line, state);
-
- var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
-
- await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
- await target.FlushAsync().ConfigureAwait(false);
- }
- }
- }
- catch (ObjectDisposedException)
- {
- // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reading ffmpeg log", ex);
- }
- }
-
- private void ParseLogLine(string line, EncodingJobInfo state)
- {
- float? framerate = null;
- double? percent = null;
- TimeSpan? transcodingPosition = null;
- long? bytesTranscoded = null;
- int? bitRate = null;
-
- var parts = line.Split(' ');
-
- var totalMs = state.RunTimeTicks.HasValue
- ? TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds
- : 0;
-
- var startMs = state.BaseRequest.StartTimeTicks.HasValue
- ? TimeSpan.FromTicks(state.BaseRequest.StartTimeTicks.Value).TotalMilliseconds
- : 0;
-
- for (var i = 0; i < parts.Length; i++)
- {
- var part = parts[i];
-
- if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) &&
- (i + 1 < parts.Length))
- {
- var rate = parts[i + 1];
- float val;
-
- if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
- {
- framerate = val;
- }
- }
- else if (state.RunTimeTicks.HasValue &&
- part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
- {
- var time = part.Split(new[] { '=' }, 2).Last();
- TimeSpan val;
-
- if (TimeSpan.TryParse(time, _usCulture, out val))
- {
- var currentMs = startMs + val.TotalMilliseconds;
-
- var percentVal = currentMs / totalMs;
- percent = 100 * percentVal;
-
- transcodingPosition = val;
- }
- }
- else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase))
- {
- var size = part.Split(new[] { '=' }, 2).Last();
-
- int? scale = null;
- if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
- {
- scale = 1024;
- size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
- }
-
- if (scale.HasValue)
- {
- long val;
-
- if (long.TryParse(size, NumberStyles.Any, _usCulture, out val))
- {
- bytesTranscoded = val * scale.Value;
- }
- }
- }
- else if (part.StartsWith("bitrate=", StringComparison.OrdinalIgnoreCase))
- {
- var rate = part.Split(new[] { '=' }, 2).Last();
-
- int? scale = null;
- if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
- {
- scale = 1024;
- rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
- }
-
- if (scale.HasValue)
- {
- float val;
-
- if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
- {
- bitRate = (int)Math.Ceiling(val * scale.Value);
- }
- }
- }
- }
-
- if (framerate.HasValue || percent.HasValue)
- {
- state.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded, bitRate);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
index b025011d7..81e294069 100644
--- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
+++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
@@ -53,8 +52,7 @@ namespace MediaBrowser.Controller.Net
public IEnumerable<string> GetRoles()
{
- return (Roles ?? string.Empty).Split(',')
- .Where(i => !string.IsNullOrWhiteSpace(i));
+ return (Roles ?? string.Empty).Split(new []{ ',' }, StringSplitOptions.RemoveEmptyEntries);
}
}
diff --git a/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs b/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs
deleted file mode 100644
index f71784d82..000000000
--- a/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Querying;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Persistence
-{
- public interface IFileOrganizationRepository
- {
- /// <summary>
- /// Saves the result.
- /// </summary>
- /// <param name="result">The result.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the specified identifier.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task Delete(string id);
-
- /// <summary>
- /// Gets the result.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>FileOrganizationResult.</returns>
- FileOrganizationResult GetResult(string id);
-
- /// <summary>
- /// Gets the results.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable{FileOrganizationResult}.</returns>
- QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query);
-
- /// <summary>
- /// Deletes all.
- /// </summary>
- /// <returns>Task.</returns>
- Task DeleteAll();
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 58ae1f3b0..bf54914b1 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="itemId">The item id.</param>
/// <returns>Task{IEnumerable{ItemReview}}.</returns>
- IEnumerable<ItemReview> GetCriticReviews(Guid itemId);
+ List<ItemReview> GetCriticReviews(Guid itemId);
/// <summary>
/// Saves the critic reviews.
@@ -65,7 +65,7 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
- IEnumerable<ChapterInfo> GetChapters(Guid id);
+ List<ChapterInfo> GetChapters(Guid id);
/// <summary>
/// Gets a single chapter for an item
@@ -78,18 +78,14 @@ namespace MediaBrowser.Controller.Persistence
/// <summary>
/// Saves the chapters.
/// </summary>
- /// <param name="id">The id.</param>
- /// <param name="chapters">The chapters.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveChapters(Guid id, List<ChapterInfo> chapters, CancellationToken cancellationToken);
+ Task SaveChapters(Guid id, List<ChapterInfo> chapters);
/// <summary>
/// Gets the media streams.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable{MediaStream}.</returns>
- IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query);
+ List<MediaStream> GetMediaStreams(MediaStreamQuery query);
/// <summary>
/// Saves the media streams.
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index c992ac56a..aec0668d4 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -105,7 +105,7 @@ namespace MediaBrowser.Controller.Playlists
if (query != null)
{
- items = items.Where(i => UserViewBuilder.FilterItem(i, query));
+ items = items.Where(i => UserViewBuilder.FilterItem(i, query)).ToList();
}
return items;
@@ -116,12 +116,12 @@ namespace MediaBrowser.Controller.Playlists
return GetLinkedChildrenInfos();
}
- private IEnumerable<BaseItem> GetPlayableItems(User user, DtoOptions options)
+ private List<BaseItem> GetPlayableItems(User user, DtoOptions options)
{
return GetPlaylistItems(MediaType, base.GetChildren(user, true), user, options);
}
- public static IEnumerable<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user, DtoOptions options)
+ public static List<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user, DtoOptions options)
{
if (user != null)
{
@@ -182,10 +182,7 @@ namespace MediaBrowser.Controller.Playlists
DtoOptions = options
};
- var itemsResult = folder.GetItems(query);
- var items = itemsResult.Items;
-
- return items;
+ return folder.GetItemList(query);
}
return new[] { item };
diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs
index b88477409..74feb4ea2 100644
--- a/MediaBrowser.Controller/Providers/AlbumInfo.cs
+++ b/MediaBrowser.Controller/Providers/AlbumInfo.cs
@@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Providers
/// Gets or sets the album artist.
/// </summary>
/// <value>The album artist.</value>
- public List<string> AlbumArtists { get; set; }
+ public string[] AlbumArtists { get; set; }
/// <summary>
/// Gets or sets the artist provider ids.
@@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Providers
{
ArtistProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
SongInfos = new List<SongInfo>();
- AlbumArtists = new List<string>();
+ AlbumArtists = EmptyStringArray;
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index 6d220f3a3..337bb23a4 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -13,10 +13,10 @@ namespace MediaBrowser.Controller.Providers
public class DirectoryService : IDirectoryService
{
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
- private readonly ConcurrentDictionary<string, Dictionary<string, FileSystemMetadata>> _cache =
- new ConcurrentDictionary<string, Dictionary<string, FileSystemMetadata>>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, FileSystemMetadata[]> _cache =
+ new ConcurrentDictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, FileSystemMetadata> _fileCache =
new ConcurrentDictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Providers
public DirectoryService(ILogger logger, IFileSystem fileSystem)
{
_logger = logger;
- _fileSystem = fileSystem;
+ _fileSystem = fileSystem;
}
public DirectoryService(IFileSystem fileSystem)
@@ -32,28 +32,23 @@ namespace MediaBrowser.Controller.Providers
{
}
- public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path)
+ public FileSystemMetadata[] GetFileSystemEntries(string path)
{
return GetFileSystemEntries(path, false);
}
- public Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(string path)
- {
- return GetFileSystemDictionary(path, false);
- }
-
- private Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(string path, bool clearCache)
+ private FileSystemMetadata[] GetFileSystemEntries(string path, bool clearCache)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException("path");
}
- Dictionary<string, FileSystemMetadata> entries;
+ FileSystemMetadata[] entries;
if (clearCache)
{
- Dictionary<string, FileSystemMetadata> removed;
+ FileSystemMetadata[] removed;
_cache.TryRemove(path, out removed);
}
@@ -62,37 +57,22 @@ namespace MediaBrowser.Controller.Providers
{
//_logger.Debug("Getting files for " + path);
- entries = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
-
try
{
// using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
- var list = _fileSystem.GetFileSystemEntries(path)
- .ToList();
-
- // Seeing dupes on some users file system for some reason
- foreach (var item in list)
- {
- entries[item.FullName] = item;
- }
+ entries = _fileSystem.GetFileSystemEntries(path).ToArray();
}
catch (IOException)
{
+ entries = new FileSystemMetadata[] { };
}
- //var group = entries.ToLookup(i => _fileSystem.GetDirectoryName(i.FullName)).ToList();
-
_cache.TryAdd(path, entries);
}
return entries;
}
- private IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool clearCache)
- {
- return GetFileSystemDictionary(path, clearCache).Values;
- }
-
public IEnumerable<FileSystemMetadata> GetFiles(string path)
{
return GetFiles(path, false);
@@ -103,16 +83,6 @@ namespace MediaBrowser.Controller.Providers
return GetFileSystemEntries(path, clearCache).Where(i => !i.IsDirectory);
}
- public IEnumerable<string> GetFilePaths(string path)
- {
- return _fileSystem.GetFilePaths(path);
- }
-
- public IEnumerable<string> GetFilePaths(string path, bool clearCache)
- {
- return _fileSystem.GetFilePaths(path);
- }
-
public FileSystemMetadata GetFile(string path)
{
FileSystemMetadata file;
@@ -133,10 +103,5 @@ namespace MediaBrowser.Controller.Providers
return file;
//return _fileSystem.GetFileInfo(path);
}
-
- public IEnumerable<FileSystemMetadata> GetDirectories(string path)
- {
- return GetFileSystemEntries(path, false).Where(i => i.IsDirectory);
- }
}
}
diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
index b8e88ea53..5df999ab0 100644
--- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs
+++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
@@ -10,7 +10,6 @@ namespace MediaBrowser.Controller.Providers
public int? IndexNumberEnd { get; set; }
public bool IsMissingEpisode { get; set; }
- public bool IsVirtualUnaired { get; set; }
public EpisodeInfo()
{
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index 1b203f32c..374703948 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -1,18 +1,12 @@
using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Providers
{
public interface IDirectoryService
{
- IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path);
- IEnumerable<FileSystemMetadata> GetDirectories(string path);
+ FileSystemMetadata[] GetFileSystemEntries(string path);
IEnumerable<FileSystemMetadata> GetFiles(string path);
- IEnumerable<string> GetFilePaths(string path);
- IEnumerable<string> GetFilePaths(string path, bool clearCache);
FileSystemMetadata GetFile(string path);
- Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(string path);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
index 9c3f94763..e96a2d65e 100644
--- a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageType}.</returns>
- IEnumerable<ImageType> GetSupportedImages(IHasImages item);
+ IEnumerable<ImageType> GetSupportedImages(IHasMetadata item);
/// <summary>
/// Gets the image.
@@ -22,6 +22,6 @@ namespace MediaBrowser.Controller.Providers
/// <param name="type">The type.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{DynamicImageResponse}.</returns>
- Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken);
+ Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs
index a43941607..a993f536d 100644
--- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs
+++ b/MediaBrowser.Controller/Providers/IImageEnhancer.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Providers
/// <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(IHasImages item, ImageType imageType);
+ bool Supports(IHasMetadata item, ImageType imageType);
/// <summary>
/// Gets the priority or order in which this enhancer should be run.
@@ -27,7 +27,7 @@ namespace MediaBrowser.Controller.Providers
/// <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(IHasImages item, ImageType imageType);
+ string GetConfigurationCacheKey(IHasMetadata item, ImageType imageType);
/// <summary>
/// Gets the size of the enhanced image.
@@ -37,7 +37,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <param name="originalImageSize">Size of the original image.</param>
/// <returns>ImageSize.</returns>
- ImageSize GetEnhancedImageSize(IHasImages item, ImageType imageType, int imageIndex, ImageSize originalImageSize);
+ ImageSize GetEnhancedImageSize(IHasMetadata item, ImageType imageType, int imageIndex, ImageSize originalImageSize);
/// <summary>
/// Enhances the image async.
@@ -49,6 +49,6 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{Image}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
- Task EnhanceImageAsync(IHasImages item, string inputFile, string outputFile, ImageType imageType, int imageIndex);
+ Task EnhanceImageAsync(IHasMetadata item, string inputFile, string outputFile, ImageType imageType, int imageIndex);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs
index 1e5bdfeaf..2b43c9cb8 100644
--- a/MediaBrowser.Controller/Providers/IImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IImageProvider.cs
@@ -18,6 +18,6 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- bool Supports(IHasImages item);
+ bool Supports(IHasMetadata item);
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
index 7e5d82843..e93c73a52 100644
--- a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
@@ -5,6 +5,6 @@ namespace MediaBrowser.Controller.Providers
{
public interface ILocalImageFileProvider : ILocalImageProvider
{
- List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService);
+ List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 0ba573da8..703666d66 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- Task SaveImage(IHasImages item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken);
+ Task SaveImage(IHasMetadata item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken);
/// <summary>
/// Saves the image.
@@ -62,13 +62,13 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken);
+ Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken);
/// <summary>
/// Saves the image.
/// </summary>
/// <returns>Task.</returns>
- Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
+ Task SaveImage(IHasMetadata item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
/// <summary>
/// Adds the metadata providers.
@@ -84,14 +84,14 @@ namespace MediaBrowser.Controller.Providers
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken);
+ Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasMetadata item, RemoteImageQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageProviderInfo}.</returns>
- IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasImages item);
+ IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasMetadata item);
/// <summary>
/// Gets all metadata plugins.
@@ -135,7 +135,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="item">The item.</param>
/// <returns>MetadataOptions.</returns>
- MetadataOptions GetMetadataOptions(IHasImages item);
+ MetadataOptions GetMetadataOptions(IHasMetadata item);
/// <summary>
/// Gets the remote search results.
diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
index 6b94547bb..86a7939e6 100644
--- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageType}.</returns>
- IEnumerable<ImageType> GetSupportedImages(IHasImages item);
+ IEnumerable<ImageType> GetSupportedImages(IHasMetadata item);
/// <summary>
/// Gets the images.
@@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken);
+ Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken);
/// <summary>
/// Gets the image response.
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index 8de11b743..63cc48058 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Providers
{
Path = item.Path;
ContainingFolderPath = item.ContainingFolderPath;
- IsInMixedFolder = item.DetectIsInMixedFolder();
+ IsInMixedFolder = item.IsInMixedFolder;
var video = item as Video;
if (video != null)
diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
index dc7a04135..98122e776 100644
--- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
@@ -6,6 +6,8 @@ namespace MediaBrowser.Controller.Providers
{
public class ItemLookupInfo : IHasProviderIds
{
+ protected static string[] EmptyStringArray = new string[] { };
+
/// <summary>
/// Gets or sets the name.
/// </summary>
diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs
index b83912a00..988e931cd 100644
--- a/MediaBrowser.Controller/Providers/SongInfo.cs
+++ b/MediaBrowser.Controller/Providers/SongInfo.cs
@@ -4,14 +4,14 @@ namespace MediaBrowser.Controller.Providers
{
public class SongInfo : ItemLookupInfo
{
- public List<string> AlbumArtists { get; set; }
+ public string[] AlbumArtists { get; set; }
public string Album { get; set; }
public List<string> Artists { get; set; }
public SongInfo()
{
Artists = new List<string>();
- AlbumArtists = new List<string>();
+ AlbumArtists = EmptyStringArray;
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
index 291632ea5..66f64464f 100644
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ b/MediaBrowser.Controller/Sync/ISyncManager.cs
@@ -16,6 +16,7 @@ namespace MediaBrowser.Controller.Sync
event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
+ event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCancelled;
/// <summary>
/// Creates the job.
@@ -135,20 +136,6 @@ namespace MediaBrowser.Controller.Sync
Task<SyncDataResponse> SyncData(SyncDataRequest request);
/// <summary>
- /// Marks the job item for removal.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task MarkJobItemForRemoval(string id);
-
- /// <summary>
- /// Unmarks the job item for removal.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task UnmarkJobItemForRemoval(string id);
-
- /// <summary>
/// Gets the library item ids.
/// </summary>
/// <param name="query">The query.</param>
diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs
deleted file mode 100644
index 8e9b2bf77..000000000
--- a/MediaBrowser.Controller/Sync/ISyncRepository.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface ISyncRepository
- {
- /// <summary>
- /// Gets the job.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SyncJob.</returns>
- SyncJob GetJob(string id);
-
- /// <summary>
- /// Creates the specified job.
- /// </summary>
- /// <param name="job">The job.</param>
- /// <returns>Task.</returns>
- Task Create(SyncJob job);
-
- /// <summary>
- /// Updates the specified job.
- /// </summary>
- /// <param name="job">The job.</param>
- /// <returns>Task.</returns>
- Task Update(SyncJob job);
-
- /// <summary>
- /// Deletes the job.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task DeleteJob(string id);
-
- /// <summary>
- /// Gets the jobs.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;SyncJob&gt;.</returns>
- QueryResult<SyncJob> GetJobs(SyncJobQuery query);
-
- /// <summary>
- /// Gets the job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SyncJobItem.</returns>
- SyncJobItem GetJobItem(string id);
-
- /// <summary>
- /// Creates the specified job item.
- /// </summary>
- /// <param name="jobItem">The job item.</param>
- /// <returns>Task.</returns>
- Task Create(SyncJobItem jobItem);
-
- /// <summary>
- /// Updates the specified job item.
- /// </summary>
- /// <param name="jobItem">The job item.</param>
- /// <returns>Task.</returns>
- Task Update(SyncJobItem jobItem);
-
- /// <summary>
- /// Gets the job items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable&lt;SyncJobItem&gt;.</returns>
- QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
-
- /// <summary>
- /// Gets the library item ids.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;System.String&gt;.</returns>
- Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query);
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 954e0c322..4ec2eeeb1 100644
--- a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.LocalMetadata.Images
get { return "Collection Folder Images"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is CollectionFolder && item.SupportsLocalMetadata;
}
@@ -35,7 +35,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
{
var collectionFolder = (CollectionFolder)item;
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index 2b5858aec..881d2a85a 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -31,12 +31,12 @@ namespace MediaBrowser.LocalMetadata.Images
get { return 0; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Episode && item.SupportsLocalMetadata;
}
- public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
{
var parentPath = _fileSystem.GetDirectoryName(item.Path);
diff --git a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
deleted file mode 100644
index 83554f044..000000000
--- a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Providers;
-
-namespace MediaBrowser.LocalMetadata.Images
-{
- public class ImagesByNameImageProvider : ILocalImageFileProvider, IHasOrder
- {
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _config;
-
- public ImagesByNameImageProvider(IFileSystem fileSystem, IServerConfigurationManager config)
- {
- _fileSystem = fileSystem;
- _config = config;
- }
-
- public string Name
- {
- get { return "Images By Name"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is CollectionFolder;
- }
-
- public int Order
- {
- get
- {
- // Run after LocalImageProvider, and after CollectionFolderImageProvider
- return 2;
- }
- }
-
- public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService)
- {
- var name = _fileSystem.GetValidFilename(item.Name);
-
- var path = Path.Combine(_config.ApplicationPaths.GeneralPath, name);
-
- try
- {
- return new LocalImageProvider(_fileSystem).GetImages(item, path, false, directoryService);
- }
- catch (IOException)
- {
- return new List<LocalImageInfo>();
- }
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 47579f870..469a31442 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.LocalMetadata.Images
get { return "Internal Images"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
if (item is Photo)
{
@@ -61,7 +61,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
{
var path = item.GetInternalMetadataPath();
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index b449ad6e0..1b61f079e 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.LocalMetadata.Images
get { return 0; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
if (item.SupportsLocalMetadata)
{
@@ -63,7 +63,7 @@ namespace MediaBrowser.LocalMetadata.Images
return false;
}
- private IEnumerable<FileSystemMetadata> GetFiles(IHasImages item, bool includeDirectories, IDirectoryService directoryService)
+ private IEnumerable<FileSystemMetadata> GetFiles(IHasMetadata item, bool includeDirectories, IDirectoryService directoryService)
{
if (item.LocationType != LocationType.FileSystem)
{
@@ -85,7 +85,7 @@ namespace MediaBrowser.LocalMetadata.Images
.OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty));
}
- public List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
{
var files = GetFiles(item, true, directoryService).ToList();
@@ -96,30 +96,14 @@ namespace MediaBrowser.LocalMetadata.Images
return list;
}
- public List<LocalImageInfo> GetImages(IHasImages item, string path, bool isPathInMediaFolder, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, string path, bool isPathInMediaFolder, IDirectoryService directoryService)
{
return GetImages(item, new[] { path }, isPathInMediaFolder, directoryService);
}
- public List<LocalImageInfo> GetImages(IHasImages item, IEnumerable<string> paths, bool arePathsInMediaFolders, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(IHasMetadata item, IEnumerable<string> paths, bool arePathsInMediaFolders, IDirectoryService directoryService)
{
- IEnumerable<FileSystemMetadata> files;
-
- if (arePathsInMediaFolders)
- {
- files = paths.SelectMany(i => _fileSystem.GetFiles(i, BaseItem.SupportedImageExtensions, true, false));
- }
- else
- {
- files = paths.SelectMany(directoryService.GetFiles)
- .Where(i =>
- {
- var ext = i.Extension;
-
- return !string.IsNullOrEmpty(ext) &&
- BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- });
- }
+ IEnumerable<FileSystemMetadata> files = paths.SelectMany(i => _fileSystem.GetFiles(i, BaseItem.SupportedImageExtensions, true, false));
files = files
.OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty));
@@ -131,7 +115,7 @@ namespace MediaBrowser.LocalMetadata.Images
return list;
}
- private void PopulateImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemMetadata> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
+ private void PopulateImages(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
{
if (supportParentSeriesFiles)
{
@@ -144,7 +128,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
var imagePrefix = item.FileNameWithoutExtension + "-";
- var isInMixedFolder = item.DetectIsInMixedFolder();
+ var isInMixedFolder = item.IsInMixedFolder;
PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder);
@@ -179,7 +163,7 @@ namespace MediaBrowser.LocalMetadata.Images
PopulateScreenshots(images, files, imagePrefix, isInMixedFolder);
}
- private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder)
+ private void PopulatePrimaryImages(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder)
{
var names = new List<string>
{
@@ -231,7 +215,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
+ private void PopulateBackdrops(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
{
if (!string.IsNullOrEmpty(item.Path))
{
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index 9a7371a66..0986ffdc2 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -39,7 +39,6 @@
<Compile Include="BaseXmlProvider.cs" />
<Compile Include="Images\CollectionFolderImageProvider.cs" />
<Compile Include="Images\EpisodeLocalImageProvider.cs" />
- <Compile Include="Images\ImagesByNameImageProvider.cs" />
<Compile Include="Images\InternalMetadataFolderImageProvider.cs" />
<Compile Include="Images\LocalImageProvider.cs" />
<Compile Include="Parsers\BaseItemXmlParser.cs" />
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 2edccf8c8..9a814213b 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -179,18 +179,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
item.Name = reader.ReadElementContentAsString();
break;
- case "Type":
- {
- var type = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(type) && !type.Equals("none", StringComparison.OrdinalIgnoreCase))
- {
- item.DisplayMediaType = type;
- }
-
- break;
- }
-
case "CriticRating":
{
var text = reader.ReadElementContentAsString();
@@ -258,7 +246,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
var person = item as Person;
if (person != null)
{
- person.ProductionLocations = new List<string> { val };
+ person.ProductionLocations = new string[] { val };
}
}
@@ -279,13 +267,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
case "LockedFields":
{
- var fields = new List<MetadataFields>();
-
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
- var list = val.Split('|').Select(i =>
+ item.LockedFields = val.Split('|').Select(i =>
{
MetadataFields field;
@@ -296,13 +282,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
return null;
- }).Where(i => i.HasValue).Select(i => i.Value);
-
- fields.AddRange(list);
+ }).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
- item.LockedFields = fields;
-
break;
}
@@ -485,7 +467,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
if (!string.IsNullOrWhiteSpace(val))
{
- hasTrailers.AddTrailerUrl(val, false);
+ hasTrailers.AddTrailerUrl(val);
}
}
break;
@@ -638,22 +620,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
- case "PlotKeywords":
- {
- if (!reader.IsEmptyElement)
- {
- using (var subtree = reader.ReadSubtree())
- {
- FetchFromKeywordsNode(subtree, item);
- }
- }
- else
- {
- reader.Read();
- }
- break;
- }
-
case "Persons":
{
if (!reader.IsEmptyElement)
@@ -961,6 +927,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
reader.MoveToContent();
reader.Read();
+ var tags = new List<string>();
+
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
@@ -974,7 +942,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
if (!string.IsNullOrWhiteSpace(tag))
{
- item.AddTag(tag);
+ tags.Add(tag);
}
break;
}
@@ -989,41 +957,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
reader.Read();
}
}
- }
- private void FetchFromKeywordsNode(XmlReader reader, BaseItem item)
- {
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "PlotKeyword":
- {
- var tag = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(tag))
- {
- item.AddKeyword(tag);
- }
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- else
- {
- reader.Read();
- }
- }
+ item.Tags = tags.Distinct(StringComparer.Ordinal).ToArray();
}
/// <summary>
@@ -1095,7 +1030,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val))
{
- item.AddTrailerUrl(val, false);
+ item.AddTrailerUrl(val);
}
break;
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
index a0f0e4476..33eb75edb 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Xml;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -84,7 +85,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
}
- item.Item.LinkedChildren = list;
+ item.Item.LinkedChildren = list.ToArray(list.Count);
}
public BoxSetXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
index 695fe2b12..c6ef85814 100644
--- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Xml;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -124,7 +125,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
}
- item.LinkedChildren = list;
+ item.LinkedChildren = list.ToArray(list.Count);
}
private void FetchFromSharesNode(XmlReader reader, Playlist item)
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index 4eec33003..259f42391 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -80,7 +80,6 @@ namespace MediaBrowser.LocalMetadata.Savers
"Overview",
"Persons",
- "PlotKeywords",
"PremiereDate",
"ProductionYear",
"Rating",
@@ -107,7 +106,6 @@ namespace MediaBrowser.LocalMetadata.Savers
"Trailers",
"TVcomId",
"TvDbId",
- "Type",
"TVRageId",
"Website",
"Zap2ItId",
@@ -304,14 +302,9 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteElementString("LockData", item.IsLocked.ToString().ToLower());
- if (item.LockedFields.Count > 0)
+ if (item.LockedFields.Length > 0)
{
- writer.WriteElementString("LockedFields", string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()));
- }
-
- if (!string.IsNullOrEmpty(item.DisplayMediaType))
- {
- writer.WriteElementString("Type", item.DisplayMediaType);
+ writer.WriteElementString("LockedFields", string.Join("|", item.LockedFields));
}
if (item.CriticRating.HasValue)
@@ -371,7 +364,7 @@ namespace MediaBrowser.LocalMetadata.Savers
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
- if (hasTrailers.RemoteTrailers.Count > 0)
+ if (hasTrailers.RemoteTrailers.Length > 0)
{
writer.WriteStartElement("Trailers");
@@ -384,7 +377,7 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- if (item.ProductionLocations.Count > 0)
+ if (item.ProductionLocations.Length > 0)
{
writer.WriteStartElement("Countries");
@@ -476,7 +469,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- if (item.Studios.Count > 0)
+ if (item.Studios.Length > 0)
{
writer.WriteStartElement("Studios");
@@ -488,7 +481,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- if (item.Tags.Count > 0)
+ if (item.Tags.Length > 0)
{
writer.WriteStartElement("Tags");
@@ -500,18 +493,6 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- if (item.Keywords.Count > 0)
- {
- writer.WriteStartElement("PlotKeywords");
-
- foreach (var tag in item.Keywords)
- {
- writer.WriteElementString("PlotKeyword", tag);
- }
-
- writer.WriteEndElement();
- }
-
var people = libraryManager.GetPeople(item);
if (people.Count > 0)
diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
index 26c4b4a93..24c5a4679 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
@@ -65,7 +65,7 @@ namespace MediaBrowser.LocalMetadata.Savers
public static string GetGameSavePath(Game item)
{
- if (item.DetectIsInMixedFolder())
+ if (item.IsInMixedFolder)
{
return Path.ChangeExtension(item.Path, ".xml");
}
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
deleted file mode 100644
index 219b1f3c5..000000000
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using BDInfo;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Text;
-
-namespace MediaBrowser.MediaEncoding.BdInfo
-{
- /// <summary>
- /// Class BdInfoExaminer
- /// </summary>
- public class BdInfoExaminer : IBlurayExaminer
- {
- private readonly IFileSystem _fileSystem;
- private readonly ITextEncoding _textEncoding;
-
- public BdInfoExaminer(IFileSystem fileSystem, ITextEncoding textEncoding)
- {
- _fileSystem = fileSystem;
- _textEncoding = textEncoding;
- }
-
- /// <summary>
- /// Gets the disc info.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>BlurayDiscInfo.</returns>
- public BlurayDiscInfo GetDiscInfo(string path)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var bdrom = new BDROM(path, _fileSystem, _textEncoding);
-
- bdrom.Scan();
-
- // Get the longest playlist
- var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid);
-
- var outputStream = new BlurayDiscInfo
- {
- MediaStreams = new List<MediaStream>()
- };
-
- if (playlist == null)
- {
- return outputStream;
- }
-
- outputStream.Chapters = playlist.Chapters;
-
- outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks;
-
- var mediaStreams = new List<MediaStream>();
-
- foreach (var stream in playlist.SortedStreams)
- {
- var videoStream = stream as TSVideoStream;
-
- if (videoStream != null)
- {
- AddVideoStream(mediaStreams, videoStream);
- continue;
- }
-
- var audioStream = stream as TSAudioStream;
-
- if (audioStream != null)
- {
- AddAudioStream(mediaStreams, audioStream);
- continue;
- }
-
- var textStream = stream as TSTextStream;
-
- if (textStream != null)
- {
- AddSubtitleStream(mediaStreams, textStream);
- continue;
- }
-
- var graphicsStream = stream as TSGraphicsStream;
-
- if (graphicsStream != null)
- {
- AddSubtitleStream(mediaStreams, graphicsStream);
- }
- }
-
- outputStream.MediaStreams = mediaStreams;
-
- outputStream.PlaylistName = playlist.Name;
-
- if (playlist.StreamClips != null && playlist.StreamClips.Any())
- {
- // Get the files in the playlist
- outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToList();
- }
-
- return outputStream;
- }
-
- /// <summary>
- /// Adds the video stream.
- /// </summary>
- /// <param name="streams">The streams.</param>
- /// <param name="videoStream">The video stream.</param>
- private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream)
- {
- var mediaStream = new MediaStream
- {
- BitRate = Convert.ToInt32(videoStream.BitRate),
- Width = videoStream.Width,
- Height = videoStream.Height,
- Codec = videoStream.CodecShortName,
- IsInterlaced = videoStream.IsInterlaced,
- Type = MediaStreamType.Video,
- Index = streams.Count
- };
-
- if (videoStream.FrameRateDenominator > 0)
- {
- float frameRateEnumerator = videoStream.FrameRateEnumerator;
- float frameRateDenominator = videoStream.FrameRateDenominator;
-
- mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator;
- }
-
- streams.Add(mediaStream);
- }
-
- /// <summary>
- /// Adds the audio stream.
- /// </summary>
- /// <param name="streams">The streams.</param>
- /// <param name="audioStream">The audio stream.</param>
- private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream)
- {
- var stream = new MediaStream
- {
- Codec = audioStream.CodecShortName,
- Language = audioStream.LanguageCode,
- Channels = audioStream.ChannelCount,
- SampleRate = audioStream.SampleRate,
- Type = MediaStreamType.Audio,
- Index = streams.Count
- };
-
- var bitrate = Convert.ToInt32(audioStream.BitRate);
-
- if (bitrate > 0)
- {
- stream.BitRate = bitrate;
- }
-
- if (audioStream.LFE > 0)
- {
- stream.Channels = audioStream.ChannelCount + 1;
- }
-
- streams.Add(stream);
- }
-
- /// <summary>
- /// Adds the subtitle stream.
- /// </summary>
- /// <param name="streams">The streams.</param>
- /// <param name="textStream">The text stream.</param>
- private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream)
- {
- streams.Add(new MediaStream
- {
- Language = textStream.LanguageCode,
- Codec = textStream.CodecShortName,
- Type = MediaStreamType.Subtitle,
- Index = streams.Count
- });
- }
-
- /// <summary>
- /// Adds the subtitle stream.
- /// </summary>
- /// <param name="streams">The streams.</param>
- /// <param name="textStream">The text stream.</param>
- private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream)
- {
- streams.Add(new MediaStream
- {
- Language = textStream.LanguageCode,
- Codec = textStream.CodecShortName,
- Type = MediaStreamType.Subtitle,
- Index = streams.Count
- });
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
deleted file mode 100644
index 16c67655a..000000000
--- a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-using System.Collections.Generic;
-using System.IO;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.MediaEncoding.Configuration
-{
- public class EncodingConfigurationFactory : IConfigurationFactory
- {
- private readonly IFileSystem _fileSystem;
-
- public EncodingConfigurationFactory(IFileSystem fileSystem)
- {
- _fileSystem = fileSystem;
- }
-
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new[]
- {
- new EncodingConfigurationStore(_fileSystem)
- };
- }
- }
-
- public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration
- {
- private readonly IFileSystem _fileSystem;
-
- public EncodingConfigurationStore(IFileSystem fileSystem)
- {
- ConfigurationType = typeof(EncodingOptions);
- Key = "encoding";
- _fileSystem = fileSystem;
- }
-
- public void Validate(object oldConfig, object newConfig)
- {
- var oldEncodingConfig = (EncodingOptions)oldConfig;
- var newEncodingConfig = (EncodingOptions)newConfig;
-
- var newPath = newEncodingConfig.TranscodingTempPath;
-
- if (!string.IsNullOrWhiteSpace(newPath)
- && !string.Equals(oldEncodingConfig.TranscodingTempPath ?? string.Empty, newPath))
- {
- // Validate
- if (!_fileSystem.DirectoryExists(newPath))
- {
- throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
deleted file mode 100644
index 566e7946d..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using System;
-using MediaBrowser.Model.Diagnostics;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class AudioEncoder : BaseEncoder
- {
- public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
- {
- }
-
- protected override string GetCommandLineArguments(EncodingJob state)
- {
- var encodingOptions = GetEncodingOptions();
-
- return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, state.OutputFilePath);
- }
-
- protected override string GetOutputFileExtension(EncodingJob state)
- {
- var ext = base.GetOutputFileExtension(state);
-
- if (!string.IsNullOrEmpty(ext))
- {
- return ext;
- }
-
- var audioCodec = state.Options.AudioCodec;
-
- if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".aac";
- }
- if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".mp3";
- }
- if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".ogg";
- }
- if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".wma";
- }
-
- return null;
- }
-
- protected override bool IsVideoEncoder
- {
- get { return false; }
- }
-
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
deleted file mode 100644
index 3672e4e84..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ /dev/null
@@ -1,375 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public abstract class BaseEncoder
- {
- protected readonly MediaEncoder MediaEncoder;
- protected readonly ILogger Logger;
- protected readonly IServerConfigurationManager ConfigurationManager;
- protected readonly IFileSystem FileSystem;
- protected readonly IIsoManager IsoManager;
- protected readonly ILibraryManager LibraryManager;
- protected readonly ISessionManager SessionManager;
- protected readonly ISubtitleEncoder SubtitleEncoder;
- protected readonly IMediaSourceManager MediaSourceManager;
- protected IProcessFactory ProcessFactory;
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- protected EncodingHelper EncodingHelper;
-
- protected BaseEncoder(MediaEncoder mediaEncoder,
- ILogger logger,
- IServerConfigurationManager configurationManager,
- IFileSystem fileSystem,
- IIsoManager isoManager,
- ILibraryManager libraryManager,
- ISessionManager sessionManager,
- ISubtitleEncoder subtitleEncoder,
- IMediaSourceManager mediaSourceManager, IProcessFactory processFactory)
- {
- MediaEncoder = mediaEncoder;
- Logger = logger;
- ConfigurationManager = configurationManager;
- FileSystem = fileSystem;
- IsoManager = isoManager;
- LibraryManager = libraryManager;
- SessionManager = sessionManager;
- SubtitleEncoder = subtitleEncoder;
- MediaSourceManager = mediaSourceManager;
- ProcessFactory = processFactory;
-
- EncodingHelper = new EncodingHelper(MediaEncoder, ConfigurationManager, FileSystem, SubtitleEncoder);
- }
-
- public async Task<EncodingJob> Start(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager, MediaEncoder)
- .CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
-
- encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(encodingJob.OutputFilePath));
-
- encodingJob.ReadInputAtNativeFramerate = options.ReadInputAtNativeFramerate;
-
- await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false);
-
- var commandLineArgs = GetCommandLineArguments(encodingJob);
-
- var process = ProcessFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
-
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
-
- IsHidden = true,
- ErrorDialog = false,
- EnableRaisingEvents = true
- });
-
- var workingDirectory = GetWorkingDirectory(options);
- if (!string.IsNullOrWhiteSpace(workingDirectory))
- {
- process.StartInfo.WorkingDirectory = workingDirectory;
- }
-
- OnTranscodeBeginning(encodingJob);
-
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- Logger.Info(commandLineLogMessage);
-
- var logFilePath = Path.Combine(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(logFilePath));
-
- // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
-
- var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await encodingJob.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationToken).ConfigureAwait(false);
-
- process.Exited += (sender, args) => OnFfMpegProcessExited(process, encodingJob);
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error starting ffmpeg", ex);
-
- OnTranscodeFailedToStart(encodingJob.OutputFilePath, encodingJob);
-
- throw;
- }
-
- cancellationToken.Register(() => Cancel(process, encodingJob));
-
- // MUST read both stdout and stderr asynchronously or a deadlock may occurr
- //process.BeginOutputReadLine();
-
- // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- new JobLogger(Logger).StartStreamingLog(encodingJob, process.StandardError.BaseStream, encodingJob.LogFileStream);
-
- // Wait for the file to exist before proceeeding
- while (!FileSystem.FileExists(encodingJob.OutputFilePath) && !encodingJob.HasExited)
- {
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
-
- return encodingJob;
- }
-
- private void Cancel(IProcess process, EncodingJob job)
- {
- Logger.Info("Killing ffmpeg process for {0}", job.OutputFilePath);
-
- //process.Kill();
- process.StandardInput.WriteLine("q");
-
- job.IsCancelled = true;
- }
-
- /// <summary>
- /// Processes the exited.
- /// </summary>
- /// <param name="process">The process.</param>
- /// <param name="job">The job.</param>
- private void OnFfMpegProcessExited(IProcess process, EncodingJob job)
- {
- job.HasExited = true;
-
- Logger.Debug("Disposing stream resources");
- job.Dispose();
-
- var isSuccesful = false;
-
- try
- {
- var exitCode = process.ExitCode;
- Logger.Info("FFMpeg exited with code {0}", exitCode);
-
- isSuccesful = exitCode == 0;
- }
- catch
- {
- Logger.Error("FFMpeg exited with an error.");
- }
-
- if (isSuccesful && !job.IsCancelled)
- {
- job.TaskCompletionSource.TrySetResult(true);
- }
- else if (job.IsCancelled)
- {
- try
- {
- DeleteFiles(job);
- }
- catch
- {
- }
- try
- {
- job.TaskCompletionSource.TrySetException(new OperationCanceledException());
- }
- catch
- {
- }
- }
- else
- {
- try
- {
- DeleteFiles(job);
- }
- catch
- {
- }
- try
- {
- job.TaskCompletionSource.TrySetException(new Exception("Encoding failed"));
- }
- catch
- {
- }
- }
-
- // This causes on exited to be called twice:
- //try
- //{
- // // Dispose the process
- // process.Dispose();
- //}
- //catch (Exception ex)
- //{
- // Logger.ErrorException("Error disposing ffmpeg.", ex);
- //}
- }
-
- protected virtual void DeleteFiles(EncodingJob job)
- {
- FileSystem.DeleteFile(job.OutputFilePath);
- }
-
- private void OnTranscodeBeginning(EncodingJob job)
- {
- job.ReportTranscodingProgress(null, null, null, null, null);
- }
-
- private void OnTranscodeFailedToStart(string path, EncodingJob job)
- {
- if (!string.IsNullOrWhiteSpace(job.Options.DeviceId))
- {
- SessionManager.ClearTranscodingInfo(job.Options.DeviceId);
- }
- }
-
- protected abstract bool IsVideoEncoder { get; }
-
- protected virtual string GetWorkingDirectory(EncodingJobOptions options)
- {
- return null;
- }
-
- protected EncodingOptions GetEncodingOptions()
- {
- return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
- }
-
- protected abstract string GetCommandLineArguments(EncodingJob job);
-
- private string GetOutputFilePath(EncodingJob state)
- {
- var folder = string.IsNullOrWhiteSpace(state.Options.OutputDirectory) ?
- ConfigurationManager.ApplicationPaths.TranscodingTempPath :
- state.Options.OutputDirectory;
-
- var outputFileExtension = GetOutputFileExtension(state);
-
- var filename = state.Id + (outputFileExtension ?? string.Empty).ToLower();
- return Path.Combine(folder, filename);
- }
-
- protected virtual string GetOutputFileExtension(EncodingJob state)
- {
- if (!string.IsNullOrWhiteSpace(state.Options.OutputContainer))
- {
- return "." + state.Options.OutputContainer;
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the name of the output video codec
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected string GetVideoDecoder(EncodingJob state)
- {
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- // Only use alternative encoders for video files.
- // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
- // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
- if (state.VideoType != VideoType.VideoFile)
- {
- return null;
- }
-
- if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
- {
- if (string.Equals(GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
- {
- switch (state.MediaSource.VideoStream.Codec.ToLower())
- {
- case "avc":
- case "h264":
- if (MediaEncoder.SupportsDecoder("h264_qsv"))
- {
- // Seeing stalls and failures with decoding. Not worth it compared to encoding.
- return "-c:v h264_qsv ";
- }
- break;
- case "mpeg2video":
- if (MediaEncoder.SupportsDecoder("mpeg2_qsv"))
- {
- return "-c:v mpeg2_qsv ";
- }
- break;
- case "vc1":
- if (MediaEncoder.SupportsDecoder("vc1_qsv"))
- {
- return "-c:v vc1_qsv ";
- }
- break;
- }
- }
- }
-
- // leave blank so ffmpeg will decide
- return null;
- }
-
- private async Task AcquireResources(EncodingJob state, CancellationToken cancellationToken)
- {
- if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
- {
- state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false);
- }
-
- if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Options.LiveStreamId))
- {
- var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
- {
- OpenToken = state.MediaSource.OpenToken
-
- }, cancellationToken).ConfigureAwait(false);
-
- EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, null);
-
- if (state.IsVideoRequest)
- {
- EncodingHelper.TryStreamCopy(state);
- }
- }
-
- if (state.MediaSource.BufferMs.HasValue)
- {
- await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
deleted file mode 100644
index 59f3576ec..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class EncoderValidator
- {
- private readonly ILogger _logger;
- private readonly IProcessFactory _processFactory;
-
- public EncoderValidator(ILogger logger, IProcessFactory processFactory)
- {
- _logger = logger;
- _processFactory = processFactory;
- }
-
- public Tuple<List<string>, List<string>> Validate(string encoderPath)
- {
- _logger.Info("Validating media encoder at {0}", encoderPath);
-
- var decoders = GetDecoders(encoderPath);
- var encoders = GetEncoders(encoderPath);
-
- _logger.Info("Encoder validation complete");
-
- return new Tuple<List<string>, List<string>>(decoders, encoders);
- }
-
- public bool ValidateVersion(string encoderAppPath, bool logOutput)
- {
- string output = string.Empty;
- try
- {
- output = GetProcessOutput(encoderAppPath, "-version");
- }
- catch (Exception ex)
- {
- if (logOutput)
- {
- _logger.ErrorException("Error validating encoder", ex);
- }
- }
-
- if (string.IsNullOrWhiteSpace(output))
- {
- return false;
- }
-
- _logger.Info("ffmpeg info: {0}", output);
-
- if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
-
- output = " " + output + " ";
-
- for (var i = 2013; i <= 2015; i++)
- {
- var yearString = i.ToString(CultureInfo.InvariantCulture);
- if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
- }
-
- return true;
- }
-
- private List<string> GetDecoders(string encoderAppPath)
- {
- string output = string.Empty;
- try
- {
- output = GetProcessOutput(encoderAppPath, "-decoders");
- }
- catch (Exception )
- {
- //_logger.ErrorException("Error detecting available decoders", ex);
- }
-
- var found = new List<string>();
- var required = new[]
- {
- "mpeg2video",
- "h264_qsv",
- "hevc_qsv",
- "mpeg2_qsv",
- "vc1_qsv",
- "h264_cuvid",
- "hevc_cuvid",
- "dts",
- "ac3",
- "aac",
- "mp3",
- "h264",
- "hevc"
- };
-
- foreach (var codec in required)
- {
- var srch = " " + codec + " ";
-
- if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
- {
- _logger.Info("Decoder available: " + codec);
- found.Add(codec);
- }
- }
-
- return found;
- }
-
- private List<string> GetEncoders(string encoderAppPath)
- {
- string output = null;
- try
- {
- output = GetProcessOutput(encoderAppPath, "-encoders");
- }
- catch
- {
- }
-
- var found = new List<string>();
- var required = new[]
- {
- "libx264",
- "libx265",
- "mpeg4",
- "msmpeg4",
- "libvpx",
- "libvpx-vp9",
- "aac",
- "libmp3lame",
- "libopus",
- "libvorbis",
- "srt",
- "h264_nvenc",
- "hevc_nvenc",
- "h264_qsv",
- "hevc_qsv",
- "h264_omx",
- "hevc_omx",
- "h264_vaapi",
- "hevc_vaapi",
- "ac3"
- };
-
- output = output ?? string.Empty;
-
- var index = 0;
-
- foreach (var codec in required)
- {
- var srch = " " + codec + " ";
-
- if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
- {
- if (index < required.Length - 1)
- {
- _logger.Info("Encoder available: " + codec);
- }
-
- found.Add(codec);
- }
- index++;
- }
-
- return found;
- }
-
- private string GetProcessOutput(string path, string arguments)
- {
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = path,
- Arguments = arguments,
- IsHidden = true,
- ErrorDialog = false,
- RedirectStandardOutput = true
- });
-
- _logger.Info("Running {0} {1}", path, arguments);
-
- using (process)
- {
- process.Start();
-
- try
- {
- return process.StandardOutput.ReadToEnd();
- }
- catch
- {
- _logger.Info("Killing process {0} {1}", path, arguments);
-
- // Hate having to do this
- try
- {
- process.Kill();
- }
- catch (Exception ex1)
- {
- _logger.ErrorException("Error killing process", ex1);
- }
-
- throw;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
deleted file mode 100644
index d53701feb..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Net;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class EncodingJob : EncodingJobInfo, IDisposable
- {
- public bool HasExited { get; internal set; }
- public bool IsCancelled { get; internal set; }
-
- public Stream LogFileStream { get; set; }
- public IProgress<double> Progress { get; set; }
- public TaskCompletionSource<bool> TaskCompletionSource;
-
- public EncodingJobOptions Options
- {
- get { return (EncodingJobOptions) BaseRequest; }
- set { BaseRequest = value; }
- }
-
- public string Id { get; set; }
-
- public string MimeType { get; set; }
- public bool EstimateContentLength { get; set; }
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
- public long? EncodingDurationTicks { get; set; }
-
- public string ItemType { get; set; }
-
- public string GetMimeType(string outputPath)
- {
- if (!string.IsNullOrEmpty(MimeType))
- {
- return MimeType;
- }
-
- return MimeTypes.GetMimeType(outputPath);
- }
-
- private readonly ILogger _logger;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) :
- base(logger, TranscodingJobType.Progressive)
- {
- _logger = logger;
- _mediaSourceManager = mediaSourceManager;
- Id = Guid.NewGuid().ToString("N");
-
- _logger = logger;
- TaskCompletionSource = new TaskCompletionSource<bool>();
- }
-
- public void Dispose()
- {
- DisposeLiveStream();
- DisposeLogStream();
- DisposeIsoMount();
- }
-
- private void DisposeLogStream()
- {
- if (LogFileStream != null)
- {
- try
- {
- LogFileStream.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing log stream", ex);
- }
-
- LogFileStream = null;
- }
- }
-
- private async void DisposeLiveStream()
- {
- if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Options.LiveStreamId) && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing media source", ex);
- }
- }
- }
-
- public string OutputFilePath { get; set; }
-
- public string ActualOutputVideoCodec
- {
- get
- {
- var codec = OutputVideoCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = VideoStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
- public string ActualOutputAudioCodec
- {
- get
- {
- var codec = OutputAudioCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = AudioStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
- public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
- {
- var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
-
- // job.Framerate = framerate;
-
- if (!percentComplete.HasValue && ticks.HasValue && RunTimeTicks.HasValue)
- {
- var pct = ticks.Value / RunTimeTicks.Value;
- percentComplete = pct * 100;
- }
-
- if (percentComplete.HasValue)
- {
- Progress.Report(percentComplete.Value);
- }
-
- // job.TranscodingPositionTicks = ticks;
- // job.BytesTranscoded = bytesTranscoded;
-
- var deviceId = Options.DeviceId;
-
- if (!string.IsNullOrWhiteSpace(deviceId))
- {
- var audioCodec = ActualOutputVideoCodec;
- var videoCodec = ActualOutputVideoCodec;
-
- // SessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
- // {
- // Bitrate = job.TotalOutputBitrate,
- // AudioCodec = audioCodec,
- // VideoCodec = videoCodec,
- // Container = job.Options.OutputContainer,
- // Framerate = framerate,
- // CompletionPercentage = percentComplete,
- // Width = job.OutputWidth,
- // Height = job.OutputHeight,
- // AudioChannels = job.OutputAudioChannels,
- // IsAudioDirect = string.Equals(job.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
- // IsVideoDirect = string.Equals(job.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
- // });
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
deleted file mode 100644
index 50d3d254a..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ /dev/null
@@ -1,305 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class EncodingJobFactory
- {
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IConfigurationManager _config;
- private readonly IMediaEncoder _mediaEncoder;
-
- protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder)
- {
- _logger = logger;
- _libraryManager = libraryManager;
- _mediaSourceManager = mediaSourceManager;
- _config = config;
- _mediaEncoder = mediaEncoder;
- }
-
- public async Task<EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var request = options;
-
- if (string.IsNullOrEmpty(request.AudioCodec))
- {
- request.AudioCodec = InferAudioCodec(request.OutputContainer);
- }
-
- var state = new EncodingJob(_logger, _mediaSourceManager)
- {
- Options = options,
- IsVideoRequest = isVideoRequest,
- Progress = progress
- };
-
- if (!string.IsNullOrWhiteSpace(request.VideoCodec))
- {
- state.SupportedVideoCodecs = request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
- }
-
- if (!string.IsNullOrWhiteSpace(request.AudioCodec))
- {
- state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault();
- }
-
- if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
- {
- state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToSubtitleCodec(i))
- ?? state.SupportedSubtitleCodecs.FirstOrDefault();
- }
-
- var item = _libraryManager.GetItemById(request.ItemId);
- state.ItemType = item.GetType().Name;
-
- state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
-
- var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
- item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
-
- if (primaryImage != null)
- {
- state.AlbumCoverPath = primaryImage.Path;
- }
-
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
- ? mediaSources.First()
- : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
-
- var videoRequest = state.Options;
-
- encodingHelper.AttachMediaSourceInfo(state, mediaSource, null);
-
- //var container = Path.GetExtension(state.RequestedUrl);
-
- //if (string.IsNullOrEmpty(container))
- //{
- // container = request.Static ?
- // state.InputContainer :
- // (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
- //}
-
- //state.OutputContainer = (container ?? string.Empty).TrimStart('.');
-
- state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.Options, state.AudioStream);
-
- state.OutputAudioCodec = state.Options.AudioCodec;
-
- state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state.Options, state.AudioStream, state.OutputAudioCodec);
-
- if (videoRequest != null)
- {
- state.OutputVideoCodec = state.Options.VideoCodec;
- state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.Options, state.VideoStream, state.OutputVideoCodec);
-
- if (state.OutputVideoBitrate.HasValue)
- {
- var resolution = ResolutionNormalizer.Normalize(
- state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
- state.OutputVideoBitrate.Value,
- state.VideoStream == null ? null : state.VideoStream.Codec,
- state.OutputVideoCodec,
- videoRequest.MaxWidth,
- videoRequest.MaxHeight);
-
- videoRequest.MaxWidth = resolution.MaxWidth;
- videoRequest.MaxHeight = resolution.MaxHeight;
- }
- }
-
- ApplyDeviceProfileSettings(state);
-
- if (videoRequest != null)
- {
- encodingHelper.TryStreamCopy(state);
- }
-
- //state.OutputFilePath = GetOutputFilePath(state);
-
- return state;
- }
-
- protected EncodingOptions GetEncodingOptions()
- {
- return _config.GetConfiguration<EncodingOptions>("encoding");
- }
-
- /// <summary>
- /// Infers the video codec.
- /// </summary>
- /// <param name="container">The container.</param>
- /// <returns>System.Nullable{VideoCodecs}.</returns>
- private static string InferVideoCodec(string container)
- {
- var ext = "." + (container ?? string.Empty);
-
- if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
- {
- return "wmv";
- }
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
- {
- return "vpx";
- }
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
- {
- return "theora";
- }
- if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
- {
- return "h264";
- }
-
- return "copy";
- }
-
- private string InferAudioCodec(string container)
- {
- var ext = "." + (container ?? string.Empty);
-
- if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
- {
- return "mp3";
- }
- if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
- {
- return "aac";
- }
- if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
- {
- return "wma";
- }
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
-
- return "copy";
- }
-
- /// <summary>
- /// Determines whether the specified stream is H264.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <returns><c>true</c> if the specified stream is H264; otherwise, <c>false</c>.</returns>
- protected bool IsH264(MediaStream stream)
- {
- var codec = stream.Codec ?? string.Empty;
-
- return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 ||
- codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1;
- }
-
- private static int GetVideoProfileScore(string profile)
- {
- var list = new List<string>
- {
- "Constrained Baseline",
- "Baseline",
- "Extended",
- "Main",
- "High",
- "Progressive High",
- "Constrained High"
- };
-
- return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
- }
-
- private void ApplyDeviceProfileSettings(EncodingJob state)
- {
- var profile = state.Options.DeviceProfile;
-
- if (profile == null)
- {
- // Don't use settings from the default profile.
- // Only use a specific profile if it was requested.
- return;
- }
-
- var audioCodec = state.ActualOutputAudioCodec;
-
- var videoCodec = state.ActualOutputVideoCodec;
- var outputContainer = state.Options.OutputContainer;
-
- var mediaProfile = state.IsVideoRequest ?
- profile.GetAudioMediaProfile(outputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate, state.OutputAudioSampleRate, state.OutputAudioBitDepth) :
- profile.GetVideoMediaProfile(outputContainer,
- audioCodec,
- videoCodec,
- state.OutputWidth,
- state.OutputHeight,
- state.TargetVideoBitDepth,
- state.OutputVideoBitrate,
- state.TargetVideoProfile,
- state.TargetVideoLevel,
- state.TargetFramerate,
- state.TargetPacketLength,
- state.TargetTimestamp,
- state.IsTargetAnamorphic,
- state.IsTargetInterlaced,
- state.TargetRefFrames,
- state.TargetVideoStreamCount,
- state.TargetAudioStreamCount,
- state.TargetVideoCodecTag,
- state.IsTargetAVC);
-
- if (mediaProfile != null)
- {
- state.MimeType = mediaProfile.MimeType;
- }
-
- var transcodingProfile = state.IsVideoRequest ?
- profile.GetAudioTranscodingProfile(outputContainer, audioCodec) :
- profile.GetVideoTranscodingProfile(outputContainer, audioCodec, videoCodec);
-
- if (transcodingProfile != null)
- {
- state.EstimateContentLength = transcodingProfile.EstimateContentLength;
- state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
- state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
- state.Options.CopyTimestamps = transcodingProfile.CopyTimestamps;
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
deleted file mode 100644
index dc3cb5f5e..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public static class EncodingUtils
- {
- public static string GetInputArgument(List<string> inputFiles, MediaProtocol protocol)
- {
- if (protocol != MediaProtocol.File)
- {
- var url = inputFiles.First();
-
- return string.Format("\"{0}\"", url);
- }
-
- return GetConcatInputArgument(inputFiles);
- }
-
- /// <summary>
- /// Gets the concat input argument.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <returns>System.String.</returns>
- private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles)
- {
- // Get all streams
- // If there's more than one we'll need to use the concat command
- if (inputFiles.Count > 1)
- {
- var files = string.Join("|", inputFiles.Select(NormalizePath).ToArray());
-
- return string.Format("concat:\"{0}\"", files);
- }
-
- // Determine the input path for video files
- return GetFileInputArgument(inputFiles[0]);
- }
-
- /// <summary>
- /// Gets the file input argument.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- private static string GetFileInputArgument(string path)
- {
- if (path.IndexOf("://") != -1)
- {
- return string.Format("\"{0}\"", path);
- }
-
- // Quotes are valid path characters in linux and they need to be escaped here with a leading \
- path = NormalizePath(path);
-
- return string.Format("file:\"{0}\"", path);
- }
-
- /// <summary>
- /// Normalizes the path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- private static string NormalizePath(string path)
- {
- // Quotes are valid path characters in linux and they need to be escaped here with a leading \
- return path.Replace("\"", "\\\"");
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
deleted file mode 100644
index e21292cbd..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Configuration;
-
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class FontConfigLoader
- {
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
- private readonly IZipClient _zipClient;
- private readonly IFileSystem _fileSystem;
-
- private readonly string[] _fontUrls =
- {
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z"
- };
-
- public FontConfigLoader(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, IZipClient zipClient, IFileSystem fileSystem)
- {
- _httpClient = httpClient;
- _appPaths = appPaths;
- _logger = logger;
- _zipClient = zipClient;
- _fileSystem = fileSystem;
- }
-
- /// <summary>
- /// Extracts the fonts.
- /// </summary>
- /// <param name="targetPath">The target path.</param>
- /// <returns>Task.</returns>
- public async Task DownloadFonts(string targetPath)
- {
- try
- {
- var fontsDirectory = Path.Combine(targetPath, "fonts");
-
- _fileSystem.CreateDirectory(fontsDirectory);
-
- const string fontFilename = "ARIALUNI.TTF";
-
- var fontFile = Path.Combine(fontsDirectory, fontFilename);
-
- if (_fileSystem.FileExists(fontFile))
- {
- await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
- }
- else
- {
- // Kick this off, but no need to wait on it
- var task = Task.Run(async () =>
- {
- await DownloadFontFile(fontsDirectory, fontFilename, new SimpleProgress<double>()).ConfigureAwait(false);
-
- await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
- });
- }
- }
- catch (HttpException ex)
- {
- // Don't let the server crash because of this
- _logger.ErrorException("Error downloading ffmpeg font files", ex);
- }
- catch (Exception ex)
- {
- // Don't let the server crash because of this
- _logger.ErrorException("Error writing ffmpeg font files", ex);
- }
- }
-
- /// <summary>
- /// Downloads the font file.
- /// </summary>
- /// <param name="fontsDirectory">The fonts directory.</param>
- /// <param name="fontFilename">The font filename.</param>
- /// <returns>Task.</returns>
- private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
- {
- var existingFile = _fileSystem
- .GetFilePaths(_appPaths.ProgramDataPath, true)
- .FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase));
-
- if (existingFile != null)
- {
- try
- {
- _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
- return;
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error copying file", ex);
- }
- }
-
- string tempFile = null;
-
- foreach (var url in _fontUrls)
- {
- progress.Report(0);
-
- try
- {
- tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- break;
- }
- catch (Exception ex)
- {
- // The core can function without the font file, so handle this
- _logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url);
- }
- }
-
- if (string.IsNullOrEmpty(tempFile))
- {
- return;
- }
-
- Extract7zArchive(tempFile, fontsDirectory);
-
- try
- {
- _fileSystem.DeleteFile(tempFile);
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
- }
- }
- private void Extract7zArchive(string archivePath, string targetPath)
- {
- _logger.Info("Extracting {0} to {1}", archivePath, targetPath);
-
- _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
- }
-
- /// <summary>
- /// Writes the font config file.
- /// </summary>
- /// <param name="fontsDirectory">The fonts directory.</param>
- /// <returns>Task.</returns>
- private async Task WriteFontConfigFile(string fontsDirectory)
- {
- const string fontConfigFilename = "fonts.conf";
- var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
-
- if (!_fileSystem.FileExists(fontConfigFile))
- {
- var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
-
- var bytes = Encoding.UTF8.GetBytes(contents);
-
- using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileOpenMode.Create, FileAccessMode.Write,
- FileShareMode.Read, true))
- {
- await fileStream.WriteAsync(bytes, 0, bytes.Length);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
deleted file mode 100644
index f1bf29d92..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ /dev/null
@@ -1,1120 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.MediaEncoding.Probing;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- /// <summary>
- /// Class MediaEncoder
- /// </summary>
- public class MediaEncoder : IMediaEncoder, IDisposable
- {
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Gets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// The _thumbnail resource pool
- /// </summary>
- private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
-
- public string FFMpegPath { get; private set; }
-
- public string FFProbePath { get; private set; }
-
- protected readonly IServerConfigurationManager ConfigurationManager;
- protected readonly IFileSystem FileSystem;
- protected readonly ILiveTvManager LiveTvManager;
- protected readonly IIsoManager IsoManager;
- protected readonly ILibraryManager LibraryManager;
- protected readonly IChannelManager ChannelManager;
- protected readonly ISessionManager SessionManager;
- protected readonly Func<ISubtitleEncoder> SubtitleEncoder;
- protected readonly Func<IMediaSourceManager> MediaSourceManager;
- private readonly IHttpClient _httpClient;
- private readonly IZipClient _zipClient;
- private readonly IProcessFactory _processFactory;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
-
- private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
- private readonly bool _hasExternalEncoder;
- private readonly string _originalFFMpegPath;
- private readonly string _originalFFProbePath;
- private readonly int DefaultImageExtractionTimeoutMs;
- private readonly bool EnableEncoderFontFile;
-
- private readonly IEnvironmentInfo _environmentInfo;
-
- public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory,
- int defaultImageExtractionTimeoutMs,
- bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
- {
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- ConfigurationManager = configurationManager;
- FileSystem = fileSystem;
- LiveTvManager = liveTvManager;
- IsoManager = isoManager;
- LibraryManager = libraryManager;
- ChannelManager = channelManager;
- SessionManager = sessionManager;
- SubtitleEncoder = subtitleEncoder;
- MediaSourceManager = mediaSourceManager;
- _httpClient = httpClient;
- _zipClient = zipClient;
- _memoryStreamProvider = memoryStreamProvider;
- _processFactory = processFactory;
- DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
- EnableEncoderFontFile = enableEncoderFontFile;
- _environmentInfo = environmentInfo;
- FFProbePath = ffProbePath;
- FFMpegPath = ffMpegPath;
- _originalFFProbePath = ffProbePath;
- _originalFFMpegPath = ffMpegPath;
-
- _hasExternalEncoder = hasExternalEncoder;
-
- SetEnvironmentVariable();
- }
-
- private readonly object _logLock = new object();
- public void SetLogFilename(string name)
- {
- lock (_logLock)
- {
- try
- {
- _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=" + name + ":level=32");
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error setting FFREPORT environment variable", ex);
- }
- }
- }
-
- public void ClearLogFilename()
- {
- lock (_logLock)
- {
- try
- {
- _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", null);
- }
- catch (Exception ex)
- {
- //_logger.ErrorException("Error setting FFREPORT environment variable", ex);
- }
- }
- }
-
- private void SetEnvironmentVariable()
- {
- try
- {
- //_environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error setting FFREPORT environment variable", ex);
- }
- try
- {
- //_environmentInfo.SetUserEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error setting FFREPORT environment variable", ex);
- }
- }
-
- public string EncoderLocationType
- {
- get
- {
- if (_hasExternalEncoder)
- {
- return "External";
- }
-
- if (string.IsNullOrWhiteSpace(FFMpegPath))
- {
- return null;
- }
-
- if (IsSystemInstalledPath(FFMpegPath))
- {
- return "System";
- }
-
- return "Custom";
- }
- }
-
- private bool IsSystemInstalledPath(string path)
- {
- if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
- {
- return true;
- }
-
- return false;
- }
-
- public async Task Init()
- {
- InitPaths();
-
- if (!string.IsNullOrWhiteSpace(FFMpegPath))
- {
- var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
-
- SetAvailableDecoders(result.Item1);
- SetAvailableEncoders(result.Item2);
-
- if (EnableEncoderFontFile)
- {
- var directory = FileSystem.GetDirectoryName(FFMpegPath);
-
- if (!string.IsNullOrWhiteSpace(directory) && FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.ProgramDataPath, directory))
- {
- await new FontConfigLoader(_httpClient, ConfigurationManager.ApplicationPaths, _logger, _zipClient, FileSystem).DownloadFonts(directory).ConfigureAwait(false);
- }
- }
- }
- }
-
- private void InitPaths()
- {
- ConfigureEncoderPaths();
-
- if (_hasExternalEncoder)
- {
- LogPaths();
- return;
- }
-
- // If the path was passed in, save it into config now.
- var encodingOptions = GetEncodingOptions();
- var appPath = encodingOptions.EncoderAppPath;
-
- var valueToSave = FFMpegPath;
-
- if (!string.IsNullOrWhiteSpace(valueToSave))
- {
- // if using system variable, don't save this.
- if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
- {
- valueToSave = null;
- }
- }
-
- if (!string.Equals(valueToSave, appPath, StringComparison.Ordinal))
- {
- encodingOptions.EncoderAppPath = valueToSave;
- ConfigurationManager.SaveConfiguration("encoding", encodingOptions);
- }
- }
-
- public void UpdateEncoderPath(string path, string pathType)
- {
- if (_hasExternalEncoder)
- {
- return;
- }
-
- _logger.Info("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
-
- Tuple<string, string> newPaths;
-
- if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
- {
- path = "ffmpeg";
-
- newPaths = TestForInstalledVersions();
- }
- else if (string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- if (!FileSystem.FileExists(path) && !FileSystem.DirectoryExists(path))
- {
- throw new ResourceNotFoundException();
- }
- newPaths = GetEncoderPaths(path);
- }
- else
- {
- throw new ArgumentException("Unexpected pathType value");
- }
-
- if (string.IsNullOrWhiteSpace(newPaths.Item1))
- {
- throw new ResourceNotFoundException("ffmpeg not found");
- }
- if (string.IsNullOrWhiteSpace(newPaths.Item2))
- {
- throw new ResourceNotFoundException("ffprobe not found");
- }
-
- path = newPaths.Item1;
-
- if (!ValidateVersion(path, true))
- {
- throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
- }
-
- var config = GetEncodingOptions();
- config.EncoderAppPath = path;
- ConfigurationManager.SaveConfiguration("encoding", config);
-
- Init();
- }
-
- private bool ValidateVersion(string path, bool logOutput)
- {
- return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
- }
-
- private void ConfigureEncoderPaths()
- {
- if (_hasExternalEncoder)
- {
- return;
- }
-
- var appPath = GetEncodingOptions().EncoderAppPath;
-
- if (string.IsNullOrWhiteSpace(appPath))
- {
- appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg");
- }
-
- var newPaths = GetEncoderPaths(appPath);
- if (string.IsNullOrWhiteSpace(newPaths.Item1) || string.IsNullOrWhiteSpace(newPaths.Item2) || IsSystemInstalledPath(appPath))
- {
- newPaths = TestForInstalledVersions();
- }
-
- if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2))
- {
- FFMpegPath = newPaths.Item1;
- FFProbePath = newPaths.Item2;
- }
-
- LogPaths();
- }
-
- private Tuple<string, string> GetEncoderPaths(string configuredPath)
- {
- var appPath = configuredPath;
-
- if (!string.IsNullOrWhiteSpace(appPath))
- {
- if (FileSystem.DirectoryExists(appPath))
- {
- return GetPathsFromDirectory(appPath);
- }
-
- if (FileSystem.FileExists(appPath))
- {
- return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
- }
- }
-
- return new Tuple<string, string>(null, null);
- }
-
- private Tuple<string, string> TestForInstalledVersions()
- {
- string encoderPath = null;
- string probePath = null;
-
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
- {
- encoderPath = _originalFFMpegPath;
- probePath = _originalFFProbePath;
- }
-
- if (string.IsNullOrWhiteSpace(encoderPath))
- {
- if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
- {
- encoderPath = "ffmpeg";
- probePath = "ffprobe";
- }
- }
-
- return new Tuple<string, string>(encoderPath, probePath);
- }
-
- private Tuple<string, string> GetPathsFromDirectory(string path)
- {
- // Since we can't predict the file extension, first try directly within the folder
- // If that doesn't pan out, then do a recursive search
- var files = FileSystem.GetFilePaths(path);
-
- var excludeExtensions = new[] { ".c" };
-
- var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
- var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
-
- if (string.IsNullOrWhiteSpace(ffmpegPath) || !FileSystem.FileExists(ffmpegPath))
- {
- files = FileSystem.GetFilePaths(path, true);
-
- ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
-
- if (!string.IsNullOrWhiteSpace(ffmpegPath))
- {
- ffprobePath = GetProbePathFromEncoderPath(ffmpegPath);
- }
- }
-
- return new Tuple<string, string>(ffmpegPath, ffprobePath);
- }
-
- private string GetProbePathFromEncoderPath(string appPath)
- {
- return FileSystem.GetFilePaths(FileSystem.GetDirectoryName(appPath))
- .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
- }
-
- private void LogPaths()
- {
- _logger.Info("FFMpeg: {0}", FFMpegPath ?? "not found");
- _logger.Info("FFProbe: {0}", FFProbePath ?? "not found");
- }
-
- private EncodingOptions GetEncodingOptions()
- {
- return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
- }
-
- private List<string> _encoders = new List<string>();
- public void SetAvailableEncoders(List<string> list)
- {
- _encoders = list.ToList();
- //_logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
- }
-
- private List<string> _decoders = new List<string>();
- public void SetAvailableDecoders(List<string> list)
- {
- _decoders = list.ToList();
- //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
- }
-
- public bool SupportsEncoder(string encoder)
- {
- return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
- }
-
- public bool SupportsDecoder(string decoder)
- {
- return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
- }
-
- public bool CanEncodeToAudioCodec(string codec)
- {
- if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
- {
- codec = "libopus";
- }
- else if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
- {
- codec = "libmp3lame";
- }
-
- return SupportsEncoder(codec);
- }
-
- public bool CanEncodeToSubtitleCodec(string codec)
- {
- // TODO
- return true;
- }
-
- /// <summary>
- /// Gets the encoder path.
- /// </summary>
- /// <value>The encoder path.</value>
- public string EncoderPath
- {
- get { return FFMpegPath; }
- }
-
- /// <summary>
- /// Gets the media info.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
- {
- var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
-
- var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames);
-
- var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
- string analyzeDuration;
-
- if (request.AnalyzeDurationMs > 0)
- {
- analyzeDuration = "-analyzeduration " +
- (request.AnalyzeDurationMs * 1000).ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- analyzeDuration = EncodingHelper.GetAnalyzeDurationArgument(inputFiles.Length);
- }
-
- probeSize = probeSize + " " + analyzeDuration;
- probeSize = probeSize.Trim();
-
- var forceEnableLogging = request.Protocol != MediaProtocol.File;
-
- return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters,
- probeSize, request.MediaType == DlnaProfileType.Audio, request.VideoType, forceEnableLogging, cancellationToken);
- }
-
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="System.ArgumentException">Unrecognized InputType</exception>
- public string GetInputArgument(string[] inputFiles, MediaProtocol protocol)
- {
- return EncodingUtils.GetInputArgument(inputFiles.ToList(), protocol);
- }
-
- /// <summary>
- /// Gets the media info internal.
- /// </summary>
- /// <returns>Task{MediaInfoResult}.</returns>
- private async Task<MediaInfo> GetMediaInfoInternal(string inputPath,
- string primaryPath,
- MediaProtocol protocol,
- bool extractChapters,
- string probeSizeArgument,
- bool isAudio,
- VideoType videoType,
- bool forceEnableLogging,
- CancellationToken cancellationToken)
- {
- var args = extractChapters
- ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format"
- : "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format";
-
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
- RedirectStandardOutput = true,
- FileName = FFProbePath,
- Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
-
- IsHidden = true,
- ErrorDialog = false,
- EnableRaisingEvents = true
- });
-
- if (forceEnableLogging)
- {
- _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- }
- else
- {
- _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- }
-
- using (var processWrapper = new ProcessWrapper(process, this, _logger))
- {
- StartProcess(processWrapper);
-
- try
- {
- //process.BeginErrorReadLine();
-
- var result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
-
- if (result == null || (result.streams == null && result.format == null))
- {
- throw new Exception("ffprobe failed - streams and format are both null.");
- }
-
- if (result.streams != null)
- {
- // Normalize aspect ratio if invalid
- foreach (var stream in result.streams)
- {
- if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
- {
- stream.display_aspect_ratio = string.Empty;
- }
- if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
- {
- stream.sample_aspect_ratio = string.Empty;
- }
- }
- }
-
- var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem, _memoryStreamProvider).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
-
- var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
-
- if (videoStream != null && !videoStream.IsInterlaced)
- {
- var isInterlaced = DetectInterlaced(mediaInfo, videoStream);
-
- if (isInterlaced)
- {
- videoStream.IsInterlaced = true;
- }
- }
-
- return mediaInfo;
- }
- catch
- {
- StopProcess(processWrapper, 100);
-
- throw;
- }
- }
- }
-
- private bool DetectInterlaced(MediaSourceInfo video, MediaStream videoStream)
- {
- // If it's mpeg based, assume true
- if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1)
- {
- var formats = (video.Container ?? string.Empty).Split(',').ToList();
- return formats.Contains("vob", StringComparer.OrdinalIgnoreCase) ||
- formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) ||
- formats.Contains("ts", StringComparer.OrdinalIgnoreCase) ||
- formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) ||
- formats.Contains("wtv", StringComparer.OrdinalIgnoreCase);
-
- }
-
- return false;
- }
-
- /// <summary>
- /// The us culture
- /// </summary>
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
- {
- return ExtractImage(new[] { path }, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
- }
-
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
- {
- return ExtractImage(inputFiles, container, null, protocol, false, threedFormat, offset, cancellationToken);
- }
-
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken)
- {
- return ExtractImage(inputFiles, container, imageStreamIndex, protocol, false, null, null, cancellationToken);
- }
-
- private async Task<string> ExtractImage(string[] inputFiles, string container, int? imageStreamIndex, MediaProtocol protocol, bool isAudio,
- Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
- {
- var inputArgument = GetInputArgument(inputFiles, protocol);
-
- if (isAudio)
- {
- if (imageStreamIndex.HasValue && imageStreamIndex.Value > 0)
- {
- // It seems for audio files we need to subtract 1 (for the audio stream??)
- imageStreamIndex = imageStreamIndex.Value - 1;
- }
- }
- else
- {
- try
- {
- return await ExtractImageInternal(inputArgument, container, imageStreamIndex, protocol, threedFormat, offset, true, cancellationToken).ConfigureAwait(false);
- }
- catch (ArgumentException)
- {
- throw;
- }
- catch
- {
- _logger.Error("I-frame image extraction failed, will attempt standard way. Input: {0}", inputArgument);
- }
- }
-
- return await ExtractImageInternal(inputArgument, container, imageStreamIndex, protocol, threedFormat, offset, false, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task<string> ExtractImageInternal(string inputPath, string container, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(inputPath))
- {
- throw new ArgumentNullException("inputPath");
- }
-
- var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(tempExtractPath));
-
- // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
- // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
- var vf = "scale=600:trunc(600/dar/2)*2";
-
- if (threedFormat.HasValue)
- {
- switch (threedFormat.Value)
- {
- case Video3DFormat.HalfSideBySide:
- vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not.
- break;
- case Video3DFormat.FullSideBySide:
- vf = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600.
- break;
- case Video3DFormat.HalfTopAndBottom:
- vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600
- break;
- case Video3DFormat.FullTopAndBottom:
- vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600
- break;
- default:
- break;
- }
- }
-
- var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
-
- var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
- var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
-
- var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
- string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
-
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
-
- if (!string.IsNullOrWhiteSpace(probeSizeArgument))
- {
- args = probeSizeArgument + " " + args;
- }
-
- if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
- {
- args = analyzeDurationArgument + " " + args;
- }
-
- if (offset.HasValue)
- {
- args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
- }
-
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- IsHidden = true,
- ErrorDialog = false
- });
-
- _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
- using (var processWrapper = new ProcessWrapper(process, this, _logger))
- {
- bool ranToCompletion;
-
- StartProcess(processWrapper);
-
- var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
- if (timeoutMs <= 0)
- {
- timeoutMs = DefaultImageExtractionTimeoutMs;
- }
-
- ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
-
- if (!ranToCompletion)
- {
- StopProcess(processWrapper, 1000);
- }
-
- var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
- var file = FileSystem.GetFileInfo(tempExtractPath);
-
- if (exitCode == -1 || !file.Exists || file.Length == 0)
- {
- var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath);
-
- _logger.Error(msg);
-
- throw new Exception(msg);
- }
-
- return tempExtractPath;
- }
- }
-
- public string GetTimeParameter(long ticks)
- {
- var time = TimeSpan.FromTicks(ticks);
-
- return GetTimeParameter(time);
- }
-
- public string GetTimeParameter(TimeSpan time)
- {
- return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
- }
-
- public async Task ExtractVideoImagesOnInterval(string[] inputFiles,
- MediaProtocol protocol,
- Video3DFormat? threedFormat,
- TimeSpan interval,
- string targetDirectory,
- string filenamePrefix,
- int? maxWidth,
- CancellationToken cancellationToken)
- {
- var resourcePool = _thumbnailResourcePool;
-
- var inputArgument = GetInputArgument(inputFiles, protocol);
-
- var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture);
-
- if (maxWidth.HasValue)
- {
- var maxWidthParam = maxWidth.Value.ToString(UsCulture);
-
- vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
- }
-
- FileSystem.CreateDirectory(targetDirectory);
- var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
-
- var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
-
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
-
- if (!string.IsNullOrWhiteSpace(probeSizeArgument))
- {
- args = probeSizeArgument + " " + args;
- }
-
- if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
- {
- args = analyzeDurationArgument + " " + args;
- }
-
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- IsHidden = true,
- ErrorDialog = false
- });
-
- _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
-
- await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- bool ranToCompletion = false;
-
- using (var processWrapper = new ProcessWrapper(process, this, _logger))
- {
- try
- {
- StartProcess(processWrapper);
-
- // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
- // but we still need to detect if the process hangs.
- // Making the assumption that as long as new jpegs are showing up, everything is good.
-
- bool isResponsive = true;
- int lastCount = 0;
-
- while (isResponsive)
- {
- if (await process.WaitForExitAsync(30000).ConfigureAwait(false))
- {
- ranToCompletion = true;
- break;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var jpegCount = FileSystem.GetFilePaths(targetDirectory)
- .Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
-
- isResponsive = (jpegCount > lastCount);
- lastCount = jpegCount;
- }
-
- if (!ranToCompletion)
- {
- StopProcess(processWrapper, 1000);
- }
- }
- finally
- {
- resourcePool.Release();
- }
-
- var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
-
- if (exitCode == -1)
- {
- var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);
-
- _logger.Error(msg);
-
- throw new Exception(msg);
- }
- }
- }
-
- public async Task<string> EncodeAudio(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var job = await new AudioEncoder(this,
- _logger,
- ConfigurationManager,
- FileSystem,
- IsoManager,
- LibraryManager,
- SessionManager,
- SubtitleEncoder(),
- MediaSourceManager(),
- _processFactory)
- .Start(options, progress, cancellationToken).ConfigureAwait(false);
-
- await job.TaskCompletionSource.Task.ConfigureAwait(false);
-
- return job.OutputFilePath;
- }
-
- public async Task<string> EncodeVideo(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var job = await new VideoEncoder(this,
- _logger,
- ConfigurationManager,
- FileSystem,
- IsoManager,
- LibraryManager,
- SessionManager,
- SubtitleEncoder(),
- MediaSourceManager(),
- _processFactory)
- .Start(options, progress, cancellationToken).ConfigureAwait(false);
-
- await job.TaskCompletionSource.Task.ConfigureAwait(false);
-
- return job.OutputFilePath;
- }
-
- private void StartProcess(ProcessWrapper process)
- {
- process.Process.Start();
-
- lock (_runningProcesses)
- {
- _runningProcesses.Add(process);
- }
- }
- private void StopProcess(ProcessWrapper process, int waitTimeMs)
- {
- try
- {
- if (process.Process.WaitForExit(waitTimeMs))
- {
- return;
- }
- }
- catch (Exception ex)
- {
- _logger.Error("Error in WaitForExit", ex);
- }
-
- try
- {
- _logger.Info("Killing ffmpeg process");
-
- process.Process.Kill();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error killing process", ex);
- }
- }
-
- private void StopProcesses()
- {
- List<ProcessWrapper> proceses;
- lock (_runningProcesses)
- {
- proceses = _runningProcesses.ToList();
- _runningProcesses.Clear();
- }
-
- foreach (var process in proceses)
- {
- if (!process.HasExited)
- {
- StopProcess(process, 500);
- }
- }
- }
-
- public string EscapeSubtitleFilterPath(string path)
- {
- // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
- // We need to double escape
-
- return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- /// <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)
- {
- StopProcesses();
- }
- }
-
- private class ProcessWrapper : IDisposable
- {
- public readonly IProcess Process;
- public bool HasExited;
- public int? ExitCode;
- private readonly MediaEncoder _mediaEncoder;
- private readonly ILogger _logger;
-
- public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder, ILogger logger)
- {
- Process = process;
- _mediaEncoder = mediaEncoder;
- _logger = logger;
- Process.Exited += Process_Exited;
- }
-
- void Process_Exited(object sender, EventArgs e)
- {
- var process = (IProcess)sender;
-
- HasExited = true;
-
- try
- {
- ExitCode = process.ExitCode;
- }
- catch
- {
- }
-
- DisposeProcess(process);
- }
-
- private void DisposeProcess(IProcess process)
- {
- lock (_mediaEncoder._runningProcesses)
- {
- _mediaEncoder._runningProcesses.Remove(this);
- }
-
- try
- {
- process.Dispose();
- }
- catch
- {
- }
- }
-
- private bool _disposed;
- private readonly object _syncLock = new object();
- public void Dispose()
- {
- lock (_syncLock)
- {
- if (!_disposed)
- {
- if (Process != null)
- {
- Process.Exited -= Process_Exited;
- DisposeProcess(Process);
- }
- }
-
- _disposed = true;
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
deleted file mode 100644
index 96c126923..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Diagnostics;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class VideoEncoder : BaseEncoder
- {
- public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
- {
- }
-
- protected override string GetCommandLineArguments(EncodingJob state)
- {
- // Get the output codec name
- var encodingOptions = GetEncodingOptions();
-
- return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, state.OutputFilePath, "superfast");
- }
-
- protected override string GetOutputFileExtension(EncodingJob state)
- {
- var ext = base.GetOutputFileExtension(state);
-
- if (!string.IsNullOrEmpty(ext))
- {
- return ext;
- }
-
- var videoCodec = state.Options.VideoCodec;
-
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- return ".ts";
- }
- if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
- {
- return ".ogv";
- }
- if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
- {
- return ".webm";
- }
- if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
- {
- return ".asf";
- }
-
- return null;
- }
-
- protected override bool IsVideoEncoder
- {
- get { return true; }
- }
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
deleted file mode 100644
index 142e1c627..000000000
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.MediaEncoding</RootNamespace>
- <AssemblyName>MediaBrowser.MediaEncoding</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>none</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="BdInfo\BdInfoExaminer.cs" />
- <Compile Include="Configuration\EncodingConfigurationFactory.cs" />
- <Compile Include="Encoder\AudioEncoder.cs" />
- <Compile Include="Encoder\BaseEncoder.cs" />
- <Compile Include="Encoder\EncodingJob.cs" />
- <Compile Include="Encoder\EncodingJobFactory.cs" />
- <Compile Include="Encoder\EncodingUtils.cs" />
- <Compile Include="Encoder\EncoderValidator.cs" />
- <Compile Include="Encoder\FontConfigLoader.cs" />
- <Compile Include="Encoder\MediaEncoder.cs" />
- <Compile Include="Encoder\VideoEncoder.cs" />
- <Compile Include="Probing\FFProbeHelpers.cs" />
- <Compile Include="Probing\InternalMediaInfoResult.cs" />
- <Compile Include="Probing\ProbeResultNormalizer.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Subtitles\ConfigurationExtension.cs" />
- <Compile Include="Subtitles\ISubtitleParser.cs" />
- <Compile Include="Subtitles\ISubtitleWriter.cs" />
- <Compile Include="Subtitles\JsonWriter.cs" />
- <Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
- <Compile Include="Subtitles\ParserValues.cs" />
- <Compile Include="Subtitles\SrtParser.cs" />
- <Compile Include="Subtitles\SrtWriter.cs" />
- <Compile Include="Subtitles\AssParser.cs" />
- <Compile Include="Subtitles\SsaParser.cs" />
- <Compile Include="Subtitles\SubtitleEncoder.cs" />
- <Compile Include="Subtitles\TtmlWriter.cs" />
- <Compile Include="Subtitles\VttWriter.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\BDInfo\BDInfo.csproj">
- <Project>{88ae38df-19d7-406f-a6a9-09527719a21e}</Project>
- <Name>BDInfo</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
- <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
- <Name>MediaBrowser.Controller</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
- <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
- <Name>OpenSubtitlesHandler</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
-</Project> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Target Name="EmitMSBuildWarning" BeforeTargets="Build">
- <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
- </Target>
-</Project> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
deleted file mode 100644
index 396c85e21..000000000
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.MediaEncoding.Probing
-{
- public static class FFProbeHelpers
- {
- /// <summary>
- /// Normalizes the FF probe result.
- /// </summary>
- /// <param name="result">The result.</param>
- public static void NormalizeFFProbeResult(InternalMediaInfoResult result)
- {
- if (result == null)
- {
- throw new ArgumentNullException("result");
- }
-
- if (result.format != null && result.format.tags != null)
- {
- result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags);
- }
-
- if (result.streams != null)
- {
- // Convert all dictionaries to case insensitive
- foreach (var stream in result.streams)
- {
- if (stream.tags != null)
- {
- stream.tags = ConvertDictionaryToCaseInSensitive(stream.tags);
- }
-
- if (stream.disposition != null)
- {
- stream.disposition = ConvertDictionaryToCaseInSensitive(stream.disposition);
- }
- }
- }
- }
-
- /// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
- /// </summary>
- /// <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)
- {
- if (tags == null)
- {
- return null;
- }
-
- string val;
-
- tags.TryGetValue(key, out val);
- return val;
- }
-
- /// <summary>
- /// Gets an int from an FFProbeResult tags dictionary
- /// </summary>
- /// <param name="tags">The tags.</param>
- /// <param name="key">The key.</param>
- /// <returns>System.Nullable{System.Int32}.</returns>
- public static int? GetDictionaryNumericValue(Dictionary<string, string> tags, string key)
- {
- var val = GetDictionaryValue(tags, key);
-
- if (!string.IsNullOrEmpty(val))
- {
- int i;
-
- if (int.TryParse(val, out i))
- {
- return i;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets a DateTime from an FFProbeResult tags dictionary
- /// </summary>
- /// <param name="tags">The tags.</param>
- /// <param name="key">The key.</param>
- /// <returns>System.Nullable{DateTime}.</returns>
- public static DateTime? GetDictionaryDateTime(Dictionary<string, string> tags, string key)
- {
- var val = GetDictionaryValue(tags, key);
-
- if (!string.IsNullOrEmpty(val))
- {
- DateTime i;
-
- if (DateTime.TryParse(val, out i))
- {
- return i.ToUniversalTime();
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Converts a dictionary to case insensitive
- /// </summary>
- /// <param name="dict">The dict.</param>
- /// <returns>Dictionary{System.StringSystem.String}.</returns>
- private static Dictionary<string, string> ConvertDictionaryToCaseInSensitive(Dictionary<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
deleted file mode 100644
index eef273250..000000000
--- a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
+++ /dev/null
@@ -1,341 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.MediaEncoding.Probing
-{
- /// <summary>
- /// Class MediaInfoResult
- /// </summary>
- public class InternalMediaInfoResult
- {
- /// <summary>
- /// Gets or sets the streams.
- /// </summary>
- /// <value>The streams.</value>
- public MediaStreamInfo[] streams { get; set; }
-
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- 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; }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
deleted file mode 100644
index 1e91a8198..000000000
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ /dev/null
@@ -1,1372 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Xml;
-using MediaBrowser.Model.IO;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Probing
-{
- public class ProbeResultNormalizer
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
-
- public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _memoryStreamProvider = memoryStreamProvider;
- }
-
- public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
- {
- var info = new MediaInfo
- {
- Path = path,
- Protocol = protocol
- };
-
- FFProbeHelpers.NormalizeFFProbeResult(data);
- SetSize(data, info);
-
- var internalStreams = data.streams ?? new MediaStreamInfo[] { };
-
- 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.Container = data.format.format_name;
-
- if (!string.IsNullOrEmpty(data.format.bit_rate))
- {
- int value;
- if (int.TryParse(data.format.bit_rate, NumberStyles.Any, _usCulture, out value))
- {
- info.Bitrate = value;
- }
- }
- }
-
- var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- var tagStreamType = isAudio ? "audio" : "video";
-
- if (data.streams != null)
- {
- var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase));
-
- if (tagStream != null && tagStream.tags != null)
- {
- foreach (var pair in tagStream.tags)
- {
- tags[pair.Key] = pair.Value;
- }
- }
- }
-
- if (data.format != null && data.format.tags != null)
- {
- foreach (var pair in data.format.tags)
- {
- tags[pair.Key] = pair.Value;
- }
- }
-
- FetchGenres(info, tags);
- var overview = FFProbeHelpers.GetDictionaryValue(tags, "synopsis");
-
- if (string.IsNullOrWhiteSpace(overview))
- {
- overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
- }
- if (string.IsNullOrWhiteSpace(overview))
- {
- overview = FFProbeHelpers.GetDictionaryValue(tags, "desc");
- }
-
- if (!string.IsNullOrWhiteSpace(overview))
- {
- info.Overview = overview;
- }
-
- var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
- if (!string.IsNullOrWhiteSpace(title))
- {
- info.Name = title;
- }
-
- info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
-
- // Several different forms of retaildate
- info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ??
- FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ??
- FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ??
- FFProbeHelpers.GetDictionaryDateTime(tags, "date");
-
- if (isAudio)
- {
- SetAudioRuntimeTicks(data, info);
-
- // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the info stream
- // so let's create a combined list of both
-
- SetAudioInfoFromTags(info, tags);
- }
- else
- {
- FetchStudios(info, tags, "copyright");
-
- var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC");
- if (!string.IsNullOrWhiteSpace(iTunEXTC))
- {
- var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- // Example
- // mpaa|G|100|For crude humor
- if (parts.Length > 1)
- {
- info.OfficialRating = parts[1];
-
- if (parts.Length > 3)
- {
- info.OfficialRatingDescription = parts[3];
- }
- }
- }
-
- var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI");
- if (!string.IsNullOrWhiteSpace(itunesXml))
- {
- FetchFromItunesInfo(itunesXml, info);
- }
-
- if (data.format != null && !string.IsNullOrEmpty(data.format.duration))
- {
- info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
- }
-
- FetchWtvInfo(info, data);
-
- if (data.Chapters != null)
- {
- info.Chapters = data.Chapters.Select(GetChapterInfo).ToList();
- }
-
- ExtractTimestamp(info);
-
- var stereoMode = GetDictionaryValue(tags, "stereo_mode");
- if (string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase))
- {
- info.Video3DFormat = Video3DFormat.FullSideBySide;
- }
-
- foreach (var mediaStream in info.MediaStreams)
- {
- if (mediaStream.Type == MediaStreamType.Audio && !mediaStream.BitRate.HasValue)
- {
- mediaStream.BitRate = GetEstimatedAudioBitrate(mediaStream.Codec, mediaStream.Channels);
- }
- }
-
- var videoStreamsBitrate = info.MediaStreams.Where(i => i.Type == MediaStreamType.Video).Select(i => i.BitRate ?? 0).Sum();
- // If ffprobe reported the container bitrate as being the same as the video stream bitrate, then it's wrong
- if (videoStreamsBitrate == (info.Bitrate ?? 0))
- {
- info.InferTotalBitrate(true);
- }
- }
-
- return info;
- }
-
- private int? GetEstimatedAudioBitrate(string codec, int? channels)
- {
- if (!channels.HasValue)
- {
- return null;
- }
-
- var channelsValue = channels.Value;
-
- if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
- {
- if (channelsValue <= 2)
- {
- return 192000;
- }
-
- if (channelsValue >= 5)
- {
- return 320000;
- }
- }
-
- return null;
- }
-
- private void FetchFromItunesInfo(string xml, MediaInfo info)
- {
- // Make things simpler and strip out the dtd
- var plistIndex = xml.IndexOf("<plist", StringComparison.OrdinalIgnoreCase);
-
- if (plistIndex != -1)
- {
- xml = xml.Substring(plistIndex);
- }
-
- xml = "<?xml version=\"1.0\"?>" + xml;
-
- // <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>cast</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Blender Foundation</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Janus Bager Kristensen</string>\n\t\t</dict>\n\t</array>\n\t<key>directors</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Sacha Goedegebure</string>\n\t\t</dict>\n\t</array>\n\t<key>studio</key>\n\t<string>Blender Foundation</string>\n</dict>\n</plist>\n
- using (var stream = _memoryStreamProvider.CreateNew(Encoding.UTF8.GetBytes(xml)))
- {
- using (var streamReader = new StreamReader(stream))
- {
- try
- {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader))
- {
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "dict":
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
- using (var subtree = reader.ReadSubtree())
- {
- ReadFromDictNode(subtree, info);
- }
- break;
- default:
- reader.Skip();
- break;
- }
- }
- else
- {
- reader.Read();
- }
- }
- }
- }
- catch (XmlException)
- {
- // I've seen probe examples where the iTunMOVI value is just "<"
- // So we should not allow this to fail the entire probing operation
- }
- }
- }
- }
-
- private void ReadFromDictNode(XmlReader reader, MediaInfo info)
- {
- string currentKey = null;
- List<NameValuePair> pairs = new List<NameValuePair>();
-
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "key":
- if (!string.IsNullOrWhiteSpace(currentKey))
- {
- ProcessPairs(currentKey, pairs, info);
- }
- currentKey = reader.ReadElementContentAsString();
- pairs = new List<NameValuePair>();
- break;
- case "string":
- var value = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(value))
- {
- pairs.Add(new NameValuePair
- {
- Name = value,
- Value = value
- });
- }
- break;
- case "array":
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
- using (var subtree = reader.ReadSubtree())
- {
- if (!string.IsNullOrWhiteSpace(currentKey))
- {
- pairs.AddRange(ReadValueArray(subtree));
- }
- }
- break;
- default:
- reader.Skip();
- break;
- }
- }
- else
- {
- reader.Read();
- }
- }
- }
-
- private List<NameValuePair> ReadValueArray(XmlReader reader)
- {
-
- List<NameValuePair> pairs = new List<NameValuePair>();
-
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "dict":
-
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
- using (var subtree = reader.ReadSubtree())
- {
- var dict = GetNameValuePair(subtree);
- if (dict != null)
- {
- pairs.Add(dict);
- }
- }
- break;
- default:
- reader.Skip();
- break;
- }
- }
- else
- {
- reader.Read();
- }
- }
-
- return pairs;
- }
-
- private void ProcessPairs(string key, List<NameValuePair> pairs, MediaInfo info)
- {
- if (string.Equals(key, "studio", StringComparison.OrdinalIgnoreCase))
- {
- foreach (var pair in pairs)
- {
- info.Studios.Add(pair.Value);
- }
-
- info.Studios = info.Studios
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- }
- else if (string.Equals(key, "screenwriters", StringComparison.OrdinalIgnoreCase))
- {
- foreach (var pair in pairs)
- {
- info.People.Add(new BaseItemPerson
- {
- Name = pair.Value,
- Type = PersonType.Writer
- });
- }
- }
- else if (string.Equals(key, "producers", StringComparison.OrdinalIgnoreCase))
- {
- foreach (var pair in pairs)
- {
- info.People.Add(new BaseItemPerson
- {
- Name = pair.Value,
- Type = PersonType.Producer
- });
- }
- }
- else if (string.Equals(key, "directors", StringComparison.OrdinalIgnoreCase))
- {
- foreach (var pair in pairs)
- {
- info.People.Add(new BaseItemPerson
- {
- Name = pair.Value,
- Type = PersonType.Director
- });
- }
- }
- }
-
- private NameValuePair GetNameValuePair(XmlReader reader)
- {
- string name = null;
- string value = null;
-
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "key":
- name = reader.ReadElementContentAsString();
- break;
- case "string":
- value = reader.ReadElementContentAsString();
- break;
- default:
- reader.Skip();
- break;
- }
- }
- else
- {
- reader.Read();
- }
- }
-
- if (string.IsNullOrWhiteSpace(name) ||
- string.IsNullOrWhiteSpace(value))
- {
- return null;
- }
-
- return new NameValuePair
- {
- Name = name,
- Value = value
- };
- }
-
- private string NormalizeSubtitleCodec(string codec)
- {
- if (string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase))
- {
- codec = "dvbsub";
- }
- else if ((codec ?? string.Empty).IndexOf("PGS", StringComparison.OrdinalIgnoreCase) != -1)
- {
- codec = "PGSSUB";
- }
- else if ((codec ?? string.Empty).IndexOf("DVD", StringComparison.OrdinalIgnoreCase) != -1)
- {
- codec = "DVDSUB";
- }
-
- return codec;
- }
-
- /// <summary>
- /// Converts ffprobe stream info to our MediaStream class
- /// </summary>
- /// <param name="isAudio">if set to <c>true</c> [is info].</param>
- /// <param name="streamInfo">The stream info.</param>
- /// <param name="formatInfo">The format info.</param>
- /// <returns>MediaStream.</returns>
- private MediaStream GetMediaStream(bool isAudio, MediaStreamInfo streamInfo, MediaFormatInfo formatInfo)
- {
- // These are mp4 chapters
- if (string.Equals(streamInfo.codec_name, "mov_text", StringComparison.OrdinalIgnoreCase))
- {
- // Edit: but these are also sometimes subtitles?
- //return null;
- }
-
- 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
- };
-
- if (string.Equals(streamInfo.is_avc, "true", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(streamInfo.is_avc, "1", StringComparison.OrdinalIgnoreCase))
- {
- stream.IsAVC = true;
- }
- else if (string.Equals(streamInfo.is_avc, "false", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(streamInfo.is_avc, "0", StringComparison.OrdinalIgnoreCase))
- {
- stream.IsAVC = false;
- }
-
- if (!string.IsNullOrWhiteSpace(streamInfo.field_order) && !string.Equals(streamInfo.field_order, "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)
- {
- stream.CodecTag = streamInfo.codec_tag_string;
- }
-
- if (streamInfo.tags != null)
- {
- 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))
- {
- stream.Type = MediaStreamType.Audio;
-
- stream.Channels = streamInfo.channels;
-
- if (!string.IsNullOrEmpty(streamInfo.sample_rate))
- {
- int value;
- if (int.TryParse(streamInfo.sample_rate, NumberStyles.Any, _usCulture, out value))
- {
- stream.SampleRate = value;
- }
- }
-
- stream.ChannelLayout = ParseChannelLayout(streamInfo.channel_layout);
-
- if (streamInfo.bits_per_sample > 0)
- {
- stream.BitDepth = streamInfo.bits_per_sample;
- }
- else if (streamInfo.bits_per_raw_sample > 0)
- {
- stream.BitDepth = streamInfo.bits_per_raw_sample;
- }
- }
- else if (string.Equals(streamInfo.codec_type, "subtitle", StringComparison.OrdinalIgnoreCase))
- {
- stream.Type = MediaStreamType.Subtitle;
- stream.Codec = NormalizeSubtitleCodec(stream.Codec);
- }
- else if (string.Equals(streamInfo.codec_type, "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);
-
- if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
- {
- stream.Type = MediaStreamType.EmbeddedImage;
- }
- else if (string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
- {
- // How to differentiate between video and embedded image?
- // The only difference I've seen thus far is presence of codec tag, also embedded images have high (unusual) framerates
- if (!string.IsNullOrWhiteSpace(stream.CodecTag))
- {
- stream.Type = MediaStreamType.Video;
- }
- else
- {
- stream.Type = MediaStreamType.EmbeddedImage;
- }
- }
- else
- {
- stream.Type = MediaStreamType.Video;
- }
-
- stream.Width = streamInfo.width;
- stream.Height = streamInfo.height;
- stream.AspectRatio = GetAspectRatio(streamInfo);
-
- if (streamInfo.bits_per_sample > 0)
- {
- stream.BitDepth = streamInfo.bits_per_sample;
- }
- else if (streamInfo.bits_per_raw_sample > 0)
- {
- stream.BitDepth = streamInfo.bits_per_raw_sample;
- }
-
- //stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
- // string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
- // 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);
-
- if (streamInfo.refs > 0)
- {
- stream.RefFrames = streamInfo.refs;
- }
- }
- else
- {
- return null;
- }
-
- // Get stream bitrate
- var bitrate = 0;
-
- if (!string.IsNullOrEmpty(streamInfo.bit_rate))
- {
- int value;
- if (int.TryParse(streamInfo.bit_rate, NumberStyles.Any, _usCulture, out value))
- {
- bitrate = value;
- }
- }
-
- if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate) && stream.Type == MediaStreamType.Video)
- {
- // If the stream info doesn't have a bitrate get the value from the media format info
- int value;
- if (int.TryParse(formatInfo.bit_rate, NumberStyles.Any, _usCulture, out value))
- {
- bitrate = value;
- }
- }
-
- if (bitrate > 0)
- {
- stream.BitRate = bitrate;
- }
-
- if (streamInfo.disposition != null)
- {
- var isDefault = GetDictionaryValue(streamInfo.disposition, "default");
- var isForced = GetDictionaryValue(streamInfo.disposition, "forced");
-
- stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase);
-
- stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase);
- }
-
- NormalizeStreamTitle(stream);
-
- return stream;
- }
-
- private void NormalizeStreamTitle(MediaStream stream)
- {
- if (string.Equals(stream.Title, "cc", StringComparison.OrdinalIgnoreCase))
- {
- stream.Title = null;
- }
-
- if (stream.Type == MediaStreamType.EmbeddedImage)
- {
- stream.Title = null;
- }
- }
-
- /// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
- /// </summary>
- /// <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)
- {
- if (tags == null)
- {
- return null;
- }
-
- string val;
-
- tags.TryGetValue(key, out val);
- return val;
- }
-
- private string ParseChannelLayout(string input)
- {
- if (string.IsNullOrEmpty(input))
- {
- return input;
- }
-
- return input.Split('(').FirstOrDefault();
- }
-
- private string GetAspectRatio(MediaStreamInfo info)
- {
- var original = info.display_aspect_ratio;
-
- int height;
- int width;
-
- var parts = (original ?? string.Empty).Split(':');
- if (!(parts.Length == 2 &&
- int.TryParse(parts[0], NumberStyles.Any, _usCulture, out width) &&
- int.TryParse(parts[1], NumberStyles.Any, _usCulture, out height) &&
- width > 0 &&
- height > 0))
- {
- width = info.width;
- height = info.height;
- }
-
- if (width > 0 && height > 0)
- {
- double ratio = width;
- ratio /= height;
-
- if (IsClose(ratio, 1.777777778, .03))
- {
- return "16:9";
- }
-
- if (IsClose(ratio, 1.3333333333, .05))
- {
- return "4:3";
- }
-
- if (IsClose(ratio, 1.41))
- {
- return "1.41:1";
- }
-
- if (IsClose(ratio, 1.5))
- {
- return "1.5:1";
- }
-
- if (IsClose(ratio, 1.6))
- {
- return "1.6:1";
- }
-
- if (IsClose(ratio, 1.66666666667))
- {
- return "5:3";
- }
-
- if (IsClose(ratio, 1.85, .02))
- {
- return "1.85:1";
- }
-
- if (IsClose(ratio, 2.35, .025))
- {
- return "2.35:1";
- }
-
- if (IsClose(ratio, 2.4, .025))
- {
- return "2.40:1";
- }
- }
-
- return original;
- }
-
- private bool IsClose(double d1, double d2, double variance = .005)
- {
- return Math.Abs(d1 - d2) <= variance;
- }
-
- /// <summary>
- /// Gets a frame rate from a string value in ffprobe output
- /// This could be a number or in the format of 2997/125.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.Nullable{System.Single}.</returns>
- private float? GetFrameRate(string value)
- {
- if (!string.IsNullOrEmpty(value))
- {
- var parts = value.Split('/');
-
- float result;
-
- if (parts.Length == 2)
- {
- result = float.Parse(parts[0], _usCulture) / float.Parse(parts[1], _usCulture);
- }
- else
- {
- result = float.Parse(parts[0], _usCulture);
- }
-
- return float.IsNaN(result) ? (float?)null : result;
- }
-
- return null;
- }
-
- private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data)
- {
- if (result.streams != null)
- {
- // Get the first info stream
- var stream = result.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase));
-
- if (stream != null)
- {
- // Get duration from stream properties
- var duration = stream.duration;
-
- // If it's not there go into format properties
- if (string.IsNullOrEmpty(duration))
- {
- duration = result.format.duration;
- }
-
- // If we got something, parse it
- if (!string.IsNullOrEmpty(duration))
- {
- data.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks;
- }
- }
- }
- }
-
- private void SetSize(InternalMediaInfoResult data, Model.MediaInfo.MediaInfo info)
- {
- if (data.format != null)
- {
- if (!string.IsNullOrEmpty(data.format.size))
- {
- info.Size = long.Parse(data.format.size, _usCulture);
- }
- else
- {
- info.Size = null;
- }
- }
- }
-
- private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
- {
- var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");
- if (!string.IsNullOrWhiteSpace(composer))
- {
- foreach (var person in Split(composer, false))
- {
- audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer });
- }
- }
-
- //var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
- //if (!string.IsNullOrWhiteSpace(conductor))
- //{
- // foreach (var person in Split(conductor, false))
- // {
- // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor });
- // }
- //}
-
- //var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
- //if (!string.IsNullOrWhiteSpace(lyricist))
- //{
- // foreach (var person in Split(lyricist, false))
- // {
- // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist });
- // }
- //}
-
- // Check for writer some music is tagged that way as alternative to composer/lyricist
- var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer");
-
- if (!string.IsNullOrWhiteSpace(writer))
- {
- foreach (var person in Split(writer, false))
- {
- audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer });
- }
- }
-
- audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album");
-
- var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists");
-
- if (!string.IsNullOrWhiteSpace(artists))
- {
- audio.Artists = SplitArtists(artists, new[] { '/', ';' }, false)
- .DistinctNames()
- .ToList();
- }
- else
- {
- var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist");
- if (string.IsNullOrWhiteSpace(artist))
- {
- audio.Artists.Clear();
- }
- else
- {
- audio.Artists = SplitArtists(artist, _nameDelimiters, true)
- .DistinctNames()
- .ToList();
- }
- }
-
- var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist");
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
- }
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
- }
-
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- audio.AlbumArtists = new List<string>();
- }
- else
- {
- audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
- .DistinctNames()
- .ToList();
-
- }
-
- if (audio.AlbumArtists.Count == 0)
- {
- audio.AlbumArtists = audio.Artists.Take(1).ToList();
- }
-
- // Track number
- audio.IndexNumber = GetDictionaryDiscValue(tags, "track");
-
- // Disc number
- audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc");
-
- // If we don't have a ProductionYear try and get it from PremiereDate
- if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue)
- {
- audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
- }
-
- // There's several values in tags may or may not be present
- FetchStudios(audio, tags, "organization");
- FetchStudios(audio, tags, "ensemble");
- FetchStudios(audio, tags, "publisher");
- FetchStudios(audio, tags, "label");
-
- // These support mulitple values, but for now we only store the first.
- var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mb);
-
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzArtist, mb);
-
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, mb);
-
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mb);
-
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzTrack, mb);
- }
-
- private string GetMultipleMusicBrainzId(string value)
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- return null;
- }
-
- return value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(i => i.Trim())
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
-
- /// <summary>
- /// Splits the specified val.
- /// </summary>
- /// <param name="val">The val.</param>
- /// <param name="allowCommaDelimiter">if set to <c>true</c> [allow comma delimiter].</param>
- /// <returns>System.String[][].</returns>
- private IEnumerable<string> Split(string val, bool allowCommaDelimiter)
- {
- // Only use the comma as a delimeter if there are no slashes or pipes.
- // We want to be careful not to split names that have commas in them
- var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i) != -1) ?
- _nameDelimiters :
- new[] { ',' };
-
- return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => i.Trim());
- }
-
- private const string ArtistReplaceValue = " | ";
-
- private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing)
- {
- if (splitFeaturing)
- {
- val = val.Replace(" featuring ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase)
- .Replace(" feat. ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase);
- }
-
- var artistsFound = new List<string>();
-
- foreach (var whitelistArtist in GetSplitWhitelist())
- {
- var originalVal = val;
- val = val.Replace(whitelistArtist, "|", StringComparison.OrdinalIgnoreCase);
-
- if (!string.Equals(originalVal, val, StringComparison.OrdinalIgnoreCase))
- {
- artistsFound.Add(whitelistArtist);
- }
- }
-
- var artists = val.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => i.Trim());
-
- artistsFound.AddRange(artists);
- return artistsFound;
- }
-
-
- private List<string> _splitWhiteList = null;
-
- private IEnumerable<string> GetSplitWhitelist()
- {
- if (_splitWhiteList == null)
- {
- _splitWhiteList = new List<string>
- {
- "AC/DC"
- };
- }
-
- return _splitWhiteList;
- }
-
- /// <summary>
- /// Gets the studios from the tags collection
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="tags">The tags.</param>
- /// <param name="tagName">Name of the tag.</param>
- private void FetchStudios(MediaInfo info, Dictionary<string, string> tags, string tagName)
- {
- var val = FFProbeHelpers.GetDictionaryValue(tags, tagName);
-
- if (!string.IsNullOrEmpty(val))
- {
- var studios = Split(val, true);
-
- foreach (var studio in studios)
- {
- // Sometimes the artist name is listed here, account for that
- if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
- if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
-
- info.Studios.Add(studio);
- }
-
- info.Studios = info.Studios
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
- }
- }
-
- /// <summary>
- /// Gets the genres from the tags collection
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="tags">The tags.</param>
- private void FetchGenres(MediaInfo info, Dictionary<string, string> tags)
- {
- var val = FFProbeHelpers.GetDictionaryValue(tags, "genre");
-
- if (!string.IsNullOrEmpty(val))
- {
- foreach (var genre in Split(val, true))
- {
- info.Genres.Add(genre);
- }
-
- info.Genres = info.Genres
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
- }
- }
-
- /// <summary>
- /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'
- /// </summary>
- /// <param name="tags">The tags.</param>
- /// <param name="tagName">Name of the tag.</param>
- /// <returns>System.Nullable{System.Int32}.</returns>
- private int? GetDictionaryDiscValue(Dictionary<string, string> tags, string tagName)
- {
- var disc = FFProbeHelpers.GetDictionaryValue(tags, tagName);
-
- if (!string.IsNullOrEmpty(disc))
- {
- disc = disc.Split('/')[0];
-
- int num;
-
- if (int.TryParse(disc, out num))
- {
- return num;
- }
- }
-
- return null;
- }
-
- private ChapterInfo GetChapterInfo(MediaChapter chapter)
- {
- var info = new ChapterInfo();
-
- if (chapter.tags != null)
- {
- string name;
- if (chapter.tags.TryGetValue("title", out name))
- {
- info.Name = name;
- }
- }
-
- // Limit accuracy to milliseconds to match xml saving
- var secondsString = chapter.start_time;
- double seconds;
-
- if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out seconds))
- {
- var ms = Math.Round(TimeSpan.FromSeconds(seconds).TotalMilliseconds);
- info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
- }
-
- return info;
- }
-
- private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
-
- private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
- {
- if (data.format == null || data.format.tags == null)
- {
- return;
- }
-
- var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre");
-
- if (!string.IsNullOrWhiteSpace(genres))
- {
- var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => i.Trim())
- .ToList();
-
- // If this is empty then don't overwrite genres that might have been fetched earlier
- if (genreList.Count > 0)
- {
- video.Genres = genreList;
- }
- }
-
- 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");
-
- if (!string.IsNullOrEmpty(people))
- {
- video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => new BaseItemPerson { Name = i.Trim(), Type = PersonType.Actor })
- .ToList();
- }
-
- var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime");
- if (!string.IsNullOrWhiteSpace(year))
- {
- int val;
-
- if (int.TryParse(year, NumberStyles.Integer, _usCulture, out val))
- {
- video.ProductionYear = val;
- }
- }
-
- var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaOriginalBroadcastDateTime");
- if (!string.IsNullOrWhiteSpace(premiereDateString))
- {
- DateTime val;
-
- // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
- // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None)
- if (DateTime.TryParse(year, null, DateTimeStyles.None, out val))
- {
- video.PremiereDate = val.ToUniversalTime();
- }
- }
-
- var description = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription");
-
- var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle");
-
- // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
-
- // Sometimes for TV Shows the Subtitle field is empty and the subtitle description contains the subtitle, extract if possible. See ticket https://mcebuddy2x.codeplex.com/workitem/1910
- // The format is -> EPISODE/TOTAL_EPISODES_IN_SEASON. SUBTITLE: DESCRIPTION
- // OR -> COMMENT. SUBTITLE: DESCRIPTION
- // e.g. -> 4/13. The Doctor's Wife: Science fiction drama. When he follows a Time Lord distress signal, the Doctor puts Amy, Rory and his beloved TARDIS in grave danger. Also in HD. [AD,S]
- // e.g. -> CBeebies Bedtime Hour. The Mystery: Animated adventures of two friends who live on an island in the middle of the big city. Some of Abney and Teal's favourite objects are missing. [S]
- if (String.IsNullOrWhiteSpace(subTitle) && !String.IsNullOrWhiteSpace(description) && description.Substring(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).Contains(":")) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename
- {
- string[] parts = description.Split(':');
- if (parts.Length > 0)
- {
- string subtitle = parts[0];
- try
- {
- if (subtitle.Contains("/")) // It contains a episode number and season number
- {
- string[] numbers = subtitle.Split(' ');
- video.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]);
- int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]);
-
- description = String.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
- }
- else
- throw new Exception(); // Switch to default parsing
- }
- catch // Default parsing
- {
- if (subtitle.Contains(".")) // skip the comment, keep the subtitle
- description = String.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first
- else
- description = subtitle.Trim(); // Clean up whitespaces and save it
- }
- }
- }
-
- if (!string.IsNullOrWhiteSpace(description))
- {
- video.Overview = description;
- }
- }
-
- private void ExtractTimestamp(MediaInfo video)
- {
- if (video.VideoType == VideoType.VideoFile)
- {
- if (string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- video.Timestamp = GetMpegTimestamp(video.Path);
-
- _logger.Debug("Video has {0} timestamp", video.Timestamp);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error extracting timestamp info from {0}", ex, video.Path);
- video.Timestamp = null;
- }
- }
- }
- }
-
- private TransportStreamTimestamp GetMpegTimestamp(string path)
- {
- var packetBuffer = new byte['Å'];
-
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
- {
- fs.Read(packetBuffer, 0, packetBuffer.Length);
- }
-
- if (packetBuffer[0] == 71)
- {
- return TransportStreamTimestamp.None;
- }
-
- if ((packetBuffer[4] == 71) && (packetBuffer['Ä'] == 71))
- {
- if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0))
- {
- return TransportStreamTimestamp.Zero;
- }
-
- return TransportStreamTimestamp.Valid;
- }
-
- return TransportStreamTimestamp.None;
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs
deleted file mode 100644
index 53f4eb403..000000000
--- a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("MediaBrowser.MediaEncoding")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.MediaEncoding")]
-[assembly: AssemblyCopyright("Copyright © 2014")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("05f49ab9-2a90-4332-9d41-7817a9cccd90")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")] \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
deleted file mode 100644
index 6d723a087..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class AssParser : ISubtitleParser
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
- {
- var trackInfo = new SubtitleTrackInfo();
- var eventIndex = 1;
- using (var reader = new StreamReader(stream))
- {
- string line;
- while (reader.ReadLine() != "[Events]")
- {}
- var headers = ParseFieldHeaders(reader.ReadLine());
-
- while ((line = reader.ReadLine()) != null)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
- if(line.StartsWith("["))
- break;
- if(string.IsNullOrEmpty(line))
- continue;
- var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
- eventIndex++;
- var sections = line.Substring(10).Split(',');
-
- subEvent.StartPositionTicks = GetTicks(sections[headers["Start"]]);
- subEvent.EndPositionTicks = GetTicks(sections[headers["End"]]);
-
- subEvent.Text = string.Join(",", sections.Skip(headers["Text"]));
- RemoteNativeFormatting(subEvent);
-
- subEvent.Text = subEvent.Text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
-
- subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w\d]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase);
-
- trackInfo.TrackEvents.Add(subEvent);
- }
- }
- return trackInfo;
- }
-
- long GetTicks(string time)
- {
- TimeSpan span;
- return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out span)
- ? span.Ticks: 0;
- }
-
- private Dictionary<string,int> ParseFieldHeaders(string line) {
- var fields = line.Substring(8).Split(',').Select(x=>x.Trim()).ToList();
-
- var result = new Dictionary<string, int> {
- {"Start", fields.IndexOf("Start")},
- {"End", fields.IndexOf("End")},
- {"Text", fields.IndexOf("Text")}
- };
- return result;
- }
-
- /// <summary>
- /// Credit: https://github.com/SubtitleEdit/subtitleedit/blob/master/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs
- /// </summary>
- private void RemoteNativeFormatting(SubtitleTrackEvent p)
- {
- int indexOfBegin = p.Text.IndexOf('{');
- string pre = string.Empty;
- while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin)
- {
- string s = p.Text.Substring(indexOfBegin);
- if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an2}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an3}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an4}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an5}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an6}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an7}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an8}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- pre = s.Substring(0, 6);
- }
- else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an2\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an3\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an4\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an5\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an6\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an7\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an8\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an9\\", StringComparison.Ordinal))
- {
- pre = s.Substring(0, 5) + "}";
- }
- int indexOfEnd = p.Text.IndexOf('}');
- p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);
-
- indexOfBegin = p.Text.IndexOf('{');
- }
- p.Text = pre + p.Text;
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs b/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs
deleted file mode 100644
index 973c653a4..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Providers;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public static class ConfigurationExtension
- {
- public static SubtitleOptions GetSubtitleConfiguration(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<SubtitleOptions>("subtitles");
- }
- }
-
- public class SubtitleConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "subtitles",
- ConfigurationType = typeof (SubtitleOptions)
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs
deleted file mode 100644
index 75de81f46..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public interface ISubtitleParser
- {
- /// <summary>
- /// Parses the specified stream.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>SubtitleTrackInfo.</returns>
- SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs
deleted file mode 100644
index e28da9185..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleWriter.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- /// <summary>
- /// Interface ISubtitleWriter
- /// </summary>
- public interface ISubtitleWriter
- {
- /// <summary>
- /// Writes the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="stream">The stream.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs
deleted file mode 100644
index 474f712f9..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System.IO;
-using System.Text;
-using System.Threading;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class JsonWriter : ISubtitleWriter
- {
- private readonly IJsonSerializer _json;
-
- public JsonWriter(IJsonSerializer json)
- {
- _json = json;
- }
-
- public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
- {
- using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- {
- var json = _json.SerializeToString(info);
-
- writer.Write(json);
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
deleted file mode 100644
index 3954897ca..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
+++ /dev/null
@@ -1,349 +0,0 @@
-using System;
-using System.Collections.Generic;
-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.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-using OpenSubtitlesHandler;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable
- {
- private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- private readonly IServerConfigurationManager _config;
- private readonly IEncryptionManager _encryption;
-
- private readonly IJsonSerializer _json;
- private readonly IFileSystem _fileSystem;
-
- public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json, IFileSystem fileSystem)
- {
- _logger = logManager.GetLogger(GetType().Name);
- _httpClient = httpClient;
- _config = config;
- _encryption = encryption;
- _json = json;
- _fileSystem = fileSystem;
-
- _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
-
- Utilities.HttpClient = httpClient;
- OpenSubtitles.SetUserAgent("mediabrowser.tv");
- }
-
- private const string PasswordHashPrefix = "h:";
- void _config_NamedConfigurationUpdating(object sender, ConfigurationUpdateEventArgs e)
- {
- if (!string.Equals(e.Key, "subtitles", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
-
- var options = (SubtitleOptions)e.NewConfiguration;
-
- if (options != null &&
- !string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) &&
- !options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
- {
- options.OpenSubtitlesPasswordHash = EncryptPassword(options.OpenSubtitlesPasswordHash);
- }
- }
-
- private string EncryptPassword(string password)
- {
- return PasswordHashPrefix + _encryption.EncryptString(password);
- }
-
- private string DecryptPassword(string password)
- {
- if (password == null ||
- !password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
- {
- return string.Empty;
- }
-
- return _encryption.DecryptString(password.Substring(2));
- }
-
- public string Name
- {
- get { return "Open Subtitles"; }
- }
-
- private SubtitleOptions GetOptions()
- {
- return _config.GetSubtitleConfiguration();
- }
-
- public IEnumerable<VideoContentType> SupportedMediaTypes
- {
- get
- {
- var options = GetOptions();
-
- if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) ||
- string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash))
- {
- return new VideoContentType[] { };
- }
-
- return new[] { VideoContentType.Episode, VideoContentType.Movie };
- }
- }
-
- public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
- {
- return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
- }
-
- private DateTime _lastRateLimitException;
- private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
- SubtitleOptions options,
- CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- var idParts = id.Split(new[] { '-' }, 3);
-
- var format = idParts[0];
- var language = idParts[1];
- var ossId = idParts[2];
-
- var downloadsList = new[] { int.Parse(ossId, _usCulture) };
-
- await Login(cancellationToken).ConfigureAwait(false);
-
- if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
- {
- throw new RateLimitExceededException("OpenSubtitles rate limit reached");
- }
-
- var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
-
- if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
- {
- _lastRateLimitException = DateTime.UtcNow;
- throw new RateLimitExceededException("OpenSubtitles rate limit reached");
- }
-
- if (!(resultDownLoad is MethodResponseSubtitleDownload))
- {
- throw new Exception("Invalid response type");
- }
-
- var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
-
- _lastRateLimitException = DateTime.MinValue;
-
- if (results.Count == 0)
- {
- var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}",
- ossId,
- resultDownLoad.Name ?? string.Empty,
- resultDownLoad.Status ?? string.Empty,
- resultDownLoad.Message ?? string.Empty);
-
- throw new ResourceNotFoundException(msg);
- }
-
- var data = Convert.FromBase64String(results.First().Data);
-
- return new SubtitleResponse
- {
- Format = format,
- Language = language,
-
- Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)))
- };
- }
-
- private DateTime _lastLogin;
- private async Task Login(CancellationToken cancellationToken)
- {
- if ((DateTime.UtcNow - _lastLogin).TotalSeconds < 60)
- {
- return;
- }
-
- var options = GetOptions();
-
- var user = options.OpenSubtitlesUsername ?? string.Empty;
- var password = DecryptPassword(options.OpenSubtitlesPasswordHash);
-
- var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false);
-
- if (!(loginResponse is MethodResponseLogIn))
- {
- throw new Exception("Authentication to OpenSubtitles failed.");
- }
-
- _lastLogin = DateTime.UtcNow;
- }
-
- public async Task<IEnumerable<NameIdPair>> GetSupportedLanguages(CancellationToken cancellationToken)
- {
- await Login(cancellationToken).ConfigureAwait(false);
-
- var result = OpenSubtitles.GetSubLanguages("en");
- if (!(result is MethodResponseGetSubLanguages))
- {
- _logger.Error("Invalid response type");
- return new List<NameIdPair>();
- }
-
- var results = ((MethodResponseGetSubLanguages)result).Languages;
-
- return results.Select(i => new NameIdPair
- {
- Name = i.LanguageName,
- Id = i.SubLanguageID
- });
- }
-
- private string NormalizeLanguage(string language)
- {
- // Problem with Greek subtitle download #1349
- if (string.Equals(language, "gre", StringComparison.OrdinalIgnoreCase))
- {
-
- return "ell";
- }
-
- return language;
- }
-
- public async Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
- {
- var imdbIdText = request.GetProviderId(MetadataProviders.Imdb);
- long imdbId = 0;
-
- switch (request.ContentType)
- {
- case VideoContentType.Episode:
- if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName))
- {
- _logger.Debug("Episode information missing");
- return new List<RemoteSubtitleInfo>();
- }
- break;
- case VideoContentType.Movie:
- if (string.IsNullOrEmpty(request.Name))
- {
- _logger.Debug("Movie name missing");
- return new List<RemoteSubtitleInfo>();
- }
- if (string.IsNullOrWhiteSpace(imdbIdText) || !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
- {
- _logger.Debug("Imdb id missing");
- return new List<RemoteSubtitleInfo>();
- }
- break;
- }
-
- if (string.IsNullOrEmpty(request.MediaPath))
- {
- _logger.Debug("Path Missing");
- return new List<RemoteSubtitleInfo>();
- }
-
- await Login(cancellationToken).ConfigureAwait(false);
-
- var subLanguageId = NormalizeLanguage(request.Language);
- string hash;
-
- using (var fileStream = _fileSystem.OpenRead(request.MediaPath))
- {
- hash = Utilities.ComputeHash(fileStream);
- }
- var fileInfo = _fileSystem.GetFileInfo(request.MediaPath);
- var movieByteSize = fileInfo.Length;
- var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : "";
- var subtitleSearchParameters = request.ContentType == VideoContentType.Episode
- ? new List<SubtitleSearchParameters> {
- new SubtitleSearchParameters(subLanguageId,
- query: request.SeriesName,
- season: request.ParentIndexNumber.Value.ToString(_usCulture),
- episode: request.IndexNumber.Value.ToString(_usCulture))
- }
- : new List<SubtitleSearchParameters> {
- new SubtitleSearchParameters(subLanguageId, imdbid: searchImdbId),
- new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId)
- };
- var parms = new List<SubtitleSearchParameters> {
- new SubtitleSearchParameters( subLanguageId,
- movieHash: hash,
- movieByteSize: movieByteSize,
- imdbid: searchImdbId ),
- };
- parms.AddRange(subtitleSearchParameters);
- var result = await OpenSubtitles.SearchSubtitlesAsync(parms.ToArray(), cancellationToken).ConfigureAwait(false);
- if (!(result is MethodResponseSubtitleSearch))
- {
- _logger.Error("Invalid response type");
- return new List<RemoteSubtitleInfo>();
- }
-
- Predicate<SubtitleSearchResult> mediaFilter =
- x =>
- request.ContentType == VideoContentType.Episode
- ? !string.IsNullOrEmpty(x.SeriesSeason) && !string.IsNullOrEmpty(x.SeriesEpisode) &&
- int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber &&
- int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber
- : !string.IsNullOrEmpty(x.IDMovieImdb) && long.Parse(x.IDMovieImdb, _usCulture) == imdbId;
-
- var results = ((MethodResponseSubtitleSearch)result).Results;
-
- // Avoid implicitly captured closure
- var hasCopy = hash;
-
- return results.Where(x => x.SubBad == "0" && mediaFilter(x) && (!request.IsPerfectMatch || string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase)))
- .OrderBy(x => (string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase) ? 0 : 1))
- .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize))
- .ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture))
- .ThenByDescending(x => double.Parse(x.SubRating, _usCulture))
- .Select(i => new RemoteSubtitleInfo
- {
- Author = i.UserNickName,
- Comment = i.SubAuthorComment,
- CommunityRating = float.Parse(i.SubRating, _usCulture),
- DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture),
- Format = i.SubFormat,
- ProviderName = Name,
- ThreeLetterISOLanguageName = i.SubLanguageID,
-
- Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitleFile,
-
- Name = i.SubFileName,
- DateCreated = DateTime.Parse(i.SubAddDate, _usCulture),
- IsHashMatch = i.MovieHash == hasCopy
-
- }).Where(i => !string.Equals(i.Format, "sub", StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Format, "idx", StringComparison.OrdinalIgnoreCase));
- }
-
- public void Dispose()
- {
- _config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs b/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs
deleted file mode 100644
index b8c2fef1e..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class ParserValues
- {
- public const string NewLine = "\r\n";
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
deleted file mode 100644
index 2a6aa993c..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Text.RegularExpressions;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class SrtParser : ISubtitleParser
- {
- private readonly ILogger _logger;
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public SrtParser(ILogger logger)
- {
- _logger = logger;
- }
-
- public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
- {
- var trackInfo = new SubtitleTrackInfo();
- using ( var reader = new StreamReader(stream))
- {
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
- var subEvent = new SubtitleTrackEvent {Id = line};
- line = reader.ReadLine();
-
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
- var time = Regex.Split(line, @"[\t ]*-->[\t ]*");
-
- if (time.Length < 2)
- {
- // This occurs when subtitle text has an empty line as part of the text.
- // Need to adjust the break statement below to resolve this.
- _logger.Warn("Unrecognized line in srt: {0}", line);
- continue;
- }
- subEvent.StartPositionTicks = GetTicks(time[0]);
- var endTime = time[1];
- var idx = endTime.IndexOf(" ", StringComparison.Ordinal);
- if (idx > 0)
- endTime = endTime.Substring(0, idx);
- subEvent.EndPositionTicks = GetTicks(endTime);
- var multiline = new List<string>();
- while ((line = reader.ReadLine()) != null)
- {
- if (string.IsNullOrEmpty(line))
- {
- break;
- }
- multiline.Add(line);
- }
- subEvent.Text = string.Join(ParserValues.NewLine, multiline);
- subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
- subEvent.Text = Regex.Replace(subEvent.Text, @"\{(?:\\\d?[\w.-]+(?:\([^\)]*\)|&H?[0-9A-Fa-f]+&|))+\}", string.Empty, RegexOptions.IgnoreCase);
- subEvent.Text = Regex.Replace(subEvent.Text, "<", "&lt;", RegexOptions.IgnoreCase);
- subEvent.Text = Regex.Replace(subEvent.Text, ">", "&gt;", RegexOptions.IgnoreCase);
- subEvent.Text = Regex.Replace(subEvent.Text, "&lt;(\\/?(font|b|u|i|s))((\\s+(\\w|\\w[\\w\\-]*\\w)(\\s*=\\s*(?:\\\".*?\\\"|'.*?'|[^'\\\">\\s]+))?)+\\s*|\\s*)(\\/?)&gt;", "<$1$3$7>", RegexOptions.IgnoreCase);
- trackInfo.TrackEvents.Add(subEvent);
- }
- }
- return trackInfo;
- }
-
- long GetTicks(string time) {
- TimeSpan span;
- return TimeSpan.TryParseExact(time, @"hh\:mm\:ss\.fff", _usCulture, out span)
- ? span.Ticks
- : (TimeSpan.TryParseExact(time, @"hh\:mm\:ss\,fff", _usCulture, out span)
- ? span.Ticks : 0);
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
deleted file mode 100644
index c05929fde..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class SrtWriter : ISubtitleWriter
- {
- public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
- {
- using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- {
- var index = 1;
-
- foreach (var trackEvent in info.TrackEvents)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- writer.WriteLine(index.ToString(CultureInfo.InvariantCulture));
- writer.WriteLine(@"{0:hh\:mm\:ss\,fff} --> {1:hh\:mm\:ss\,fff}", TimeSpan.FromTicks(trackEvent.StartPositionTicks), TimeSpan.FromTicks(trackEvent.EndPositionTicks));
-
- var text = trackEvent.Text;
-
- // TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
-
- writer.WriteLine(text);
- writer.WriteLine(string.Empty);
-
- index++;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
deleted file mode 100644
index 6c760658d..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ /dev/null
@@ -1,394 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System;
-using System.IO;
-using System.Text;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- /// <summary>
- /// Credit to https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs
- /// </summary>
- public class SsaParser : ISubtitleParser
- {
- public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
- {
- var trackInfo = new SubtitleTrackInfo();
-
- using (var reader = new StreamReader(stream))
- {
- bool eventsStarted = false;
-
- string[] format = "Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text".Split(',');
- int indexLayer = 0;
- int indexStart = 1;
- int indexEnd = 2;
- int indexStyle = 3;
- int indexName = 4;
- int indexEffect = 8;
- int indexText = 9;
- int lineNumber = 0;
-
- var header = new StringBuilder();
-
- string line;
-
- while ((line = reader.ReadLine()) != null)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- lineNumber++;
- if (!eventsStarted)
- header.AppendLine(line);
-
- if (line.Trim().ToLower() == "[events]")
- {
- eventsStarted = true;
- }
- else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";"))
- {
- // skip comment lines
- }
- else if (eventsStarted && line.Trim().Length > 0)
- {
- string s = line.Trim().ToLower();
- if (s.StartsWith("format:"))
- {
- if (line.Length > 10)
- {
- format = line.ToLower().Substring(8).Split(',');
- for (int i = 0; i < format.Length; i++)
- {
- if (format[i].Trim().ToLower() == "layer")
- indexLayer = i;
- else if (format[i].Trim().ToLower() == "start")
- indexStart = i;
- else if (format[i].Trim().ToLower() == "end")
- indexEnd = i;
- else if (format[i].Trim().ToLower() == "text")
- indexText = i;
- else if (format[i].Trim().ToLower() == "effect")
- indexEffect = i;
- else if (format[i].Trim().ToLower() == "style")
- indexStyle = i;
- }
- }
- }
- else if (!string.IsNullOrEmpty(s))
- {
- string text = string.Empty;
- string start = string.Empty;
- string end = string.Empty;
- string style = string.Empty;
- string layer = string.Empty;
- string effect = string.Empty;
- string name = string.Empty;
-
- string[] splittedLine;
-
- if (s.StartsWith("dialogue:"))
- splittedLine = line.Substring(10).Split(',');
- else
- splittedLine = line.Split(',');
-
- for (int i = 0; i < splittedLine.Length; i++)
- {
- if (i == indexStart)
- start = splittedLine[i].Trim();
- else if (i == indexEnd)
- end = splittedLine[i].Trim();
- else if (i == indexLayer)
- layer = splittedLine[i];
- else if (i == indexEffect)
- effect = splittedLine[i];
- else if (i == indexText)
- text = splittedLine[i];
- else if (i == indexStyle)
- style = splittedLine[i];
- else if (i == indexName)
- name = splittedLine[i];
- else if (i > indexText)
- text += "," + splittedLine[i];
- }
-
- try
- {
- var p = new SubtitleTrackEvent();
-
- p.StartPositionTicks = GetTimeCodeFromString(start);
- p.EndPositionTicks = GetTimeCodeFromString(end);
- p.Text = GetFormattedText(text);
-
- trackInfo.TrackEvents.Add(p);
- }
- catch
- {
- }
- }
- }
- }
-
- //if (header.Length > 0)
- //subtitle.Header = header.ToString();
-
- //subtitle.Renumber(1);
- }
- return trackInfo;
- }
-
- private static long GetTimeCodeFromString(string time)
- {
- // h:mm:ss.cc
- string[] timeCode = time.Split(':', '.');
- return new TimeSpan(0, int.Parse(timeCode[0]),
- int.Parse(timeCode[1]),
- int.Parse(timeCode[2]),
- int.Parse(timeCode[3]) * 10).Ticks;
- }
-
- public static string GetFormattedText(string text)
- {
- text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
-
- bool italic = false;
-
- for (int i = 0; i < 10; i++) // just look ten times...
- {
- if (text.Contains(@"{\fn"))
- {
- int start = text.IndexOf(@"{\fn");
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fn}"))
- {
- string fontName = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontName, ref extraTags, out italic);
- text = text.Remove(start, end - start + 1);
- if (italic)
- text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + "><i>");
- else
- text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
-
- int indexOfEndTag = text.IndexOf("{\\fn}", start);
- if (indexOfEndTag > 0)
- text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
- else
- text += "</font>";
- }
- }
-
- if (text.Contains(@"{\fs"))
- {
- int start = text.IndexOf(@"{\fs");
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fs}"))
- {
- string fontSize = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontSize, ref extraTags, out italic);
- if (IsInteger(fontSize))
- {
- text = text.Remove(start, end - start + 1);
- if (italic)
- text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + "><i>");
- else
- text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
-
- int indexOfEndTag = text.IndexOf("{\\fs}", start);
- if (indexOfEndTag > 0)
- text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
- else
- text += "</font>";
- }
- }
- }
-
- if (text.Contains(@"{\c"))
- {
- int start = text.IndexOf(@"{\c");
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\c}"))
- {
- string color = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out italic);
-
- color = color.Replace("&", string.Empty).TrimStart('H');
- color = color.PadLeft(6, '0');
-
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLower();
-
- text = text.Remove(start, end - start + 1);
- if (italic)
- text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
- else
- text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
- int indexOfEndTag = text.IndexOf("{\\c}", start);
- if (indexOfEndTag > 0)
- text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
- else
- text += "</font>";
- }
- }
-
- if (text.Contains(@"{\1c")) // "1" specifices primary color
- {
- int start = text.IndexOf(@"{\1c");
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\1c}"))
- {
- string color = text.Substring(start + 5, end - (start + 5));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out italic);
-
- color = color.Replace("&", string.Empty).TrimStart('H');
- color = color.PadLeft(6, '0');
-
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLower();
-
- text = text.Remove(start, end - start + 1);
- if (italic)
- text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
- else
- text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
- text += "</font>";
- }
- }
-
- }
-
- text = text.Replace(@"{\i1}", "<i>");
- text = text.Replace(@"{\i0}", "</i>");
- text = text.Replace(@"{\i}", "</i>");
- if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
- text += "</i>";
-
- text = text.Replace(@"{\u1}", "<u>");
- text = text.Replace(@"{\u0}", "</u>");
- text = text.Replace(@"{\u}", "</u>");
- if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
- text += "</u>";
-
- text = text.Replace(@"{\b1}", "<b>");
- text = text.Replace(@"{\b0}", "</b>");
- text = text.Replace(@"{\b}", "</b>");
- if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
- text += "</b>";
-
- return text;
- }
-
- private static bool IsInteger(string s)
- {
- int i;
- if (int.TryParse(s, out i))
- return true;
- return false;
- }
-
- private static int CountTagInText(string text, string tag)
- {
- int count = 0;
- int index = text.IndexOf(tag);
- while (index >= 0)
- {
- count++;
- if (index == text.Length)
- return count;
- index = text.IndexOf(tag, index + 1);
- }
- return count;
- }
-
- private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic)
- {
- italic = false;
- int indexOfSPlit = tagName.IndexOf(@"\");
- if (indexOfSPlit > 0)
- {
- string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
- tagName = tagName.Remove(indexOfSPlit);
-
- for (int i = 0; i < 10; i++)
- {
- if (rest.StartsWith("fs") && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf(@"\");
- string fontSize = rest;
- if (indexOfSPlit > 0)
- {
- fontSize = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- extraTags += " size=\"" + fontSize.Substring(2) + "\"";
- }
- else if (rest.StartsWith("fn") && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf(@"\");
- string fontName = rest;
- if (indexOfSPlit > 0)
- {
- fontName = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- extraTags += " face=\"" + fontName.Substring(2) + "\"";
- }
- else if (rest.StartsWith("c") && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf(@"\");
- string fontColor = rest;
- if (indexOfSPlit > 0)
- {
- fontColor = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
-
- string color = fontColor.Substring(2);
- color = color.Replace("&", string.Empty).TrimStart('H');
- color = color.PadLeft(6, '0');
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLower();
-
- extraTags += " color=\"" + color + "\"";
- }
- else if (rest.StartsWith("i1") && rest.Length > 1)
- {
- indexOfSPlit = rest.IndexOf(@"\");
- italic = true;
- if (indexOfSPlit > 0)
- {
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- }
- else if (rest.Length > 0 && rest.Contains("\\"))
- {
- indexOfSPlit = rest.IndexOf(@"\");
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
deleted file mode 100644
index 247c5274f..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ /dev/null
@@ -1,738 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Text;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class SubtitleEncoder : ISubtitleEncoder
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly IJsonSerializer _json;
- private readonly IHttpClient _httpClient;
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
- private readonly IProcessFactory _processFactory;
- private readonly ITextEncoding _textEncoding;
-
- public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory, ITextEncoding textEncoding)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- _mediaEncoder = mediaEncoder;
- _json = json;
- _httpClient = httpClient;
- _mediaSourceManager = mediaSourceManager;
- _memoryStreamProvider = memoryStreamProvider;
- _processFactory = processFactory;
- _textEncoding = textEncoding;
- }
-
- private string SubtitleCachePath
- {
- get
- {
- return Path.Combine(_appPaths.DataPath, "subtitles");
- }
- }
-
- private Stream ConvertSubtitles(Stream stream,
- string inputFormat,
- string outputFormat,
- long startTimeTicks,
- long? endTimeTicks,
- bool preserveOriginalTimestamps,
- CancellationToken cancellationToken)
- {
- var ms = _memoryStreamProvider.CreateNew();
-
- try
- {
- var reader = GetReader(inputFormat, true);
-
- var trackInfo = reader.Parse(stream, cancellationToken);
-
- FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps);
-
- var writer = GetWriter(outputFormat);
-
- writer.Write(trackInfo, ms, cancellationToken);
- ms.Position = 0;
- }
- catch
- {
- ms.Dispose();
- throw;
- }
-
- return ms;
- }
-
- private void FilterEvents(SubtitleTrackInfo track, long startPositionTicks, long? endTimeTicks, bool preserveTimestamps)
- {
- // Drop subs that are earlier than what we're looking for
- track.TrackEvents = track.TrackEvents
- .SkipWhile(i => (i.StartPositionTicks - startPositionTicks) < 0 || (i.EndPositionTicks - startPositionTicks) < 0)
- .ToList();
-
- if (endTimeTicks.HasValue)
- {
- var endTime = endTimeTicks.Value;
-
- track.TrackEvents = track.TrackEvents
- .TakeWhile(i => i.StartPositionTicks <= endTime)
- .ToList();
- }
-
- if (!preserveTimestamps)
- {
- foreach (var trackEvent in track.TrackEvents)
- {
- trackEvent.EndPositionTicks -= startPositionTicks;
- trackEvent.StartPositionTicks -= startPositionTicks;
- }
- }
- }
-
- public async Task<Stream> GetSubtitles(string itemId,
- string mediaSourceId,
- int subtitleStreamIndex,
- string outputFormat,
- long startTimeTicks,
- long? endTimeTicks,
- bool preserveOriginalTimestamps,
- CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(itemId))
- {
- throw new ArgumentNullException("itemId");
- }
- if (string.IsNullOrWhiteSpace(mediaSourceId))
- {
- throw new ArgumentNullException("mediaSourceId");
- }
-
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = mediaSources
- .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
-
- var subtitleStream = mediaSource.MediaStreams
- .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex);
-
- var subtitle = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken)
- .ConfigureAwait(false);
-
- var inputFormat = subtitle.Item2;
- var writer = TryGetWriter(outputFormat);
-
- // Return the original if we don't have any way of converting it
- if (writer == null)
- {
- return subtitle.Item1;
- }
-
- // Return the original if the same format is being requested
- // Character encoding was already handled in GetSubtitleStream
- if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase))
- {
- return subtitle.Item1;
- }
-
- using (var stream = subtitle.Item1)
- {
- return ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken);
- }
- }
-
- private async Task<Tuple<Stream, string>> GetSubtitleStream(MediaSourceInfo mediaSource,
- MediaStream subtitleStream,
- CancellationToken cancellationToken)
- {
- var inputFiles = new[] { mediaSource.Path };
-
- if (mediaSource.VideoType.HasValue)
- {
- if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
- {
- var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id));
- inputFiles = mediaSourceItem.GetPlayableStreamFiles().ToArray();
- }
- }
-
- var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
-
- var stream = await GetSubtitleStream(fileInfo.Item1, subtitleStream.Language, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false);
-
- return new Tuple<Stream, string>(stream, fileInfo.Item3);
- }
-
- private async Task<Stream> GetSubtitleStream(string path, string language, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken)
- {
- if (requiresCharset)
- {
- var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false);
-
- var charset = _textEncoding.GetDetectedEncodingName(bytes, language, true);
- _logger.Debug("charset {0} detected for {1}", charset ?? "null", path);
-
- if (!string.IsNullOrEmpty(charset))
- {
- using (var inputStream = _memoryStreamProvider.CreateNew(bytes))
- {
- using (var reader = new StreamReader(inputStream, _textEncoding.GetEncodingFromCharset(charset)))
- {
- var text = await reader.ReadToEndAsync().ConfigureAwait(false);
-
- bytes = Encoding.UTF8.GetBytes(text);
-
- return _memoryStreamProvider.CreateNew(bytes);
- }
- }
- }
- }
-
- return _fileSystem.OpenRead(path);
- }
-
- private async Task<Tuple<string, MediaProtocol, string, bool>> GetReadableFile(string mediaPath,
- string[] inputFiles,
- MediaProtocol protocol,
- MediaStream subtitleStream,
- CancellationToken cancellationToken)
- {
- if (!subtitleStream.IsExternal)
- {
- string outputFormat;
- string outputCodec;
-
- if (string.Equals(subtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(subtitleStream.Codec, "srt", StringComparison.OrdinalIgnoreCase))
- {
- // Extract
- outputCodec = "copy";
- outputFormat = subtitleStream.Codec;
- }
- else if (string.Equals(subtitleStream.Codec, "subrip", StringComparison.OrdinalIgnoreCase))
- {
- // Extract
- outputCodec = "copy";
- outputFormat = "srt";
- }
- else
- {
- // Extract
- outputCodec = "srt";
- outputFormat = "srt";
- }
-
- // Extract
- var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat);
-
- await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
- .ConfigureAwait(false);
-
- return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, outputFormat, false);
- }
-
- var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec)
- .TrimStart('.');
-
- if (GetReader(currentFormat, false) == null)
- {
- // Convert
- var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt");
-
- await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, protocol, outputPath, cancellationToken).ConfigureAwait(false);
-
- return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, "srt", true);
- }
-
- return new Tuple<string, MediaProtocol, string, bool>(subtitleStream.Path, protocol, currentFormat, true);
- }
-
- private ISubtitleParser GetReader(string format, bool throwIfMissing)
- {
- if (string.IsNullOrEmpty(format))
- {
- throw new ArgumentNullException("format");
- }
-
- if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
- {
- return new SrtParser(_logger);
- }
- if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
- {
- return new SsaParser();
- }
- if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
- {
- return new AssParser();
- }
-
- if (throwIfMissing)
- {
- throw new ArgumentException("Unsupported format: " + format);
- }
-
- return null;
- }
-
- private ISubtitleWriter TryGetWriter(string format)
- {
- if (string.IsNullOrEmpty(format))
- {
- throw new ArgumentNullException("format");
- }
-
- if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
- {
- return new JsonWriter(_json);
- }
- if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
- {
- return new SrtWriter();
- }
- if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase))
- {
- return new VttWriter();
- }
- if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase))
- {
- return new TtmlWriter();
- }
-
- return null;
- }
-
- private ISubtitleWriter GetWriter(string format)
- {
- var writer = TryGetWriter(format);
-
- if (writer != null)
- {
- return writer;
- }
-
- throw new ArgumentException("Unsupported format: " + format);
- }
-
- /// <summary>
- /// The _semaphoreLocks
- /// </summary>
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
- new ConcurrentDictionary<string, SemaphoreSlim>();
-
- /// <summary>
- /// Gets the lock.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.Object.</returns>
- private SemaphoreSlim GetLock(string filename)
- {
- return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
- }
-
- /// <summary>
- /// Converts the text subtitle to SRT.
- /// </summary>
- /// <param name="inputPath">The input path.</param>
- /// <param name="inputProtocol">The input protocol.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
- {
- var semaphore = GetLock(outputPath);
-
- await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- if (!_fileSystem.FileExists(outputPath))
- {
- await ConvertTextSubtitleToSrtInternal(inputPath, language, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false);
- }
- }
- finally
- {
- semaphore.Release();
- }
- }
-
- /// <summary>
- /// Converts the text subtitle to SRT internal.
- /// </summary>
- /// <param name="inputPath">The input path.</param>
- /// <param name="inputProtocol">The input protocol.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">
- /// inputPath
- /// or
- /// outputPath
- /// </exception>
- private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(inputPath))
- {
- throw new ArgumentNullException("inputPath");
- }
-
- if (string.IsNullOrEmpty(outputPath))
- {
- throw new ArgumentNullException("outputPath");
- }
-
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
-
- var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, inputProtocol, cancellationToken).ConfigureAwait(false);
-
- if (!string.IsNullOrEmpty(encodingParam))
- {
- encodingParam = " -sub_charenc " + encodingParam;
- }
-
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = _mediaEncoder.EncoderPath,
- Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
-
- IsHidden = true,
- ErrorDialog = false
- });
-
- _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error starting ffmpeg", ex);
-
- throw;
- }
-
- var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
-
- if (!ranToCompletion)
- {
- try
- {
- _logger.Info("Killing ffmpeg subtitle conversion process");
-
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error killing subtitle conversion process", ex);
- }
- }
-
- var exitCode = ranToCompletion ? process.ExitCode : -1;
-
- process.Dispose();
-
- var failed = false;
-
- if (exitCode == -1)
- {
- failed = true;
-
- if (_fileSystem.FileExists(outputPath))
- {
- try
- {
- _logger.Info("Deleting converted subtitle due to failure: ", outputPath);
- _fileSystem.DeleteFile(outputPath);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting converted subtitle {0}", ex, outputPath);
- }
- }
- }
- else if (!_fileSystem.FileExists(outputPath))
- {
- failed = true;
- }
-
- if (failed)
- {
- var msg = string.Format("ffmpeg subtitle conversion failed for {0}", inputPath);
-
- _logger.Error(msg);
-
- throw new Exception(msg);
- }
- await SetAssFont(outputPath).ConfigureAwait(false);
-
- _logger.Info("ffmpeg subtitle conversion succeeded for {0}", inputPath);
- }
-
- /// <summary>
- /// Extracts the text subtitle.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
- /// <param name="outputCodec">The output codec.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
- private async Task ExtractTextSubtitle(string[] inputFiles, MediaProtocol protocol, int subtitleStreamIndex,
- string outputCodec, string outputPath, CancellationToken cancellationToken)
- {
- var semaphore = GetLock(outputPath);
-
- await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- if (!_fileSystem.FileExists(outputPath))
- {
- await ExtractTextSubtitleInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), subtitleStreamIndex, outputCodec, outputPath, cancellationToken).ConfigureAwait(false);
- }
- }
- finally
- {
- semaphore.Release();
- }
- }
-
- private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex,
- string outputCodec, string outputPath, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(inputPath))
- {
- throw new ArgumentNullException("inputPath");
- }
-
- if (string.IsNullOrEmpty(outputPath))
- {
- throw new ArgumentNullException("outputPath");
- }
-
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
-
- var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath,
- subtitleStreamIndex, outputCodec, outputPath);
-
- var process = _processFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- FileName = _mediaEncoder.EncoderPath,
- Arguments = processArgs,
- IsHidden = true,
- ErrorDialog = false
- });
-
- _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error starting ffmpeg", ex);
-
- throw;
- }
-
- var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
-
- if (!ranToCompletion)
- {
- try
- {
- _logger.Info("Killing ffmpeg subtitle extraction process");
-
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error killing subtitle extraction process", ex);
- }
- }
-
- var exitCode = ranToCompletion ? process.ExitCode : -1;
-
- process.Dispose();
-
- var failed = false;
-
- if (exitCode == -1)
- {
- failed = true;
-
- try
- {
- _logger.Info("Deleting extracted subtitle due to failure: {0}", outputPath);
- _fileSystem.DeleteFile(outputPath);
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting extracted subtitle {0}", ex, outputPath);
- }
- }
- else if (!_fileSystem.FileExists(outputPath))
- {
- failed = true;
- }
-
- if (failed)
- {
- var msg = string.Format("ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath);
-
- _logger.Error(msg);
-
- throw new Exception(msg);
- }
- else
- {
- var msg = string.Format("ffmpeg subtitle extraction completed for {0} to {1}", inputPath, outputPath);
-
- _logger.Info(msg);
- }
-
- if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase))
- {
- await SetAssFont(outputPath).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Sets the ass font.
- /// </summary>
- /// <param name="file">The file.</param>
- /// <returns>Task.</returns>
- private async Task SetAssFont(string file)
- {
- _logger.Info("Setting ass font within {0}", file);
-
- string text;
- Encoding encoding;
-
- using (var fileStream = _fileSystem.OpenRead(file))
- {
- using (var reader = new StreamReader(fileStream, true))
- {
- encoding = reader.CurrentEncoding;
-
- text = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
- }
-
- var newText = text.Replace(",Arial,", ",Arial Unicode MS,");
-
- if (!string.Equals(text, newText))
- {
- using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- using (var writer = new StreamWriter(fileStream, encoding))
- {
- writer.Write(newText);
- }
- }
- }
- }
-
- private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension)
- {
- if (protocol == MediaProtocol.File)
- {
- var ticksParam = string.Empty;
-
- var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
-
- var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension;
-
- var prefix = filename.Substring(0, 1);
-
- return Path.Combine(SubtitleCachePath, prefix, filename);
- }
- else
- {
- var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5() + outputSubtitleExtension;
-
- var prefix = filename.Substring(0, 1);
-
- return Path.Combine(SubtitleCachePath, prefix, filename);
- }
- }
-
- public async Task<string> GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken)
- {
- var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false);
-
- var charset = _textEncoding.GetDetectedEncodingName(bytes, language, true);
-
- _logger.Debug("charset {0} detected for {1}", charset ?? "null", path);
-
- return charset;
- }
-
- private async Task<byte[]> GetBytes(string path, MediaProtocol protocol, CancellationToken cancellationToken)
- {
- if (protocol == MediaProtocol.Http)
- {
- using (var file = await _httpClient.Get(path, cancellationToken).ConfigureAwait(false))
- {
- using (var memoryStream = new MemoryStream())
- {
- await file.CopyToAsync(memoryStream).ConfigureAwait(false);
- memoryStream.Position = 0;
-
- return memoryStream.ToArray();
- }
- }
- }
- if (protocol == MediaProtocol.File)
- {
- return _fileSystem.ReadAllBytes(path);
- }
-
- throw new ArgumentOutOfRangeException("protocol");
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
deleted file mode 100644
index c32005f89..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.IO;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class TtmlWriter : ISubtitleWriter
- {
- public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
- {
- // Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml
- // Parser example: https://github.com/mozilla/popcorn-js/blob/master/parsers/parserTTML/popcorn.parserTTML.js
-
- using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- {
- writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
- writer.WriteLine("<tt xmlns=\"http://www.w3.org/ns/ttml\" xmlns:tts=\"http://www.w3.org/2006/04/ttaf1#styling\" lang=\"no\">");
-
- writer.WriteLine("<head>");
- writer.WriteLine("<styling>");
- writer.WriteLine("<style id=\"italic\" tts:fontStyle=\"italic\" />");
- writer.WriteLine("<style id=\"left\" tts:textAlign=\"left\" />");
- writer.WriteLine("<style id=\"center\" tts:textAlign=\"center\" />");
- writer.WriteLine("<style id=\"right\" tts:textAlign=\"right\" />");
- writer.WriteLine("</styling>");
- writer.WriteLine("</head>");
-
- writer.WriteLine("<body>");
- writer.WriteLine("<div>");
-
- foreach (var trackEvent in info.TrackEvents)
- {
- var text = trackEvent.Text;
-
- text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase);
-
- writer.WriteLine("<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
- trackEvent.StartPositionTicks,
- (trackEvent.EndPositionTicks - trackEvent.StartPositionTicks),
- text);
- }
-
- writer.WriteLine("</div>");
- writer.WriteLine("</body>");
-
- writer.WriteLine("</tt>");
- }
- }
-
- private string FormatTime(long ticks)
- {
- var time = TimeSpan.FromTicks(ticks);
-
- return string.Format(@"{0:hh\:mm\:ss\,fff}", time);
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
deleted file mode 100644
index 092add992..000000000
--- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.IO;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.Subtitles
-{
- public class VttWriter : ISubtitleWriter
- {
- public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
- {
- using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- {
- writer.WriteLine("WEBVTT");
- writer.WriteLine(string.Empty);
- foreach (var trackEvent in info.TrackEvents)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- TimeSpan startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks);
- TimeSpan endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks);
-
- // make sure the start and end times are different and seqential
- if (endTime.TotalMilliseconds <= startTime.TotalMilliseconds)
- {
- endTime = startTime.Add(TimeSpan.FromMilliseconds(1));
- }
-
- writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff}", startTime, endTime);
-
- var text = trackEvent.Text;
-
- // TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
-
- writer.WriteLine(text);
- writer.WriteLine(string.Empty);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config
deleted file mode 100644
index 6b8deb9c9..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/ApiClient/ApiClientExtensions.cs b/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs
deleted file mode 100644
index 4ae4fe822..000000000
--- a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Class ApiClientExtensions
- /// </summary>
- public static class ApiClientExtensions
- {
- /// <summary>
- /// Gets the image stream async.
- /// </summary>
- /// <param name="apiClient">The API client.</param>
- /// <param name="url">The URL.</param>
- /// <returns>Task{Stream}.</returns>
- public static Task<Stream> GetImageStreamAsync(this IApiClient apiClient, string url)
- {
- return apiClient.GetImageStreamAsync(url, CancellationToken.None);
- }
-
- public static Task<UserDto[]> GetPublicUsersAsync(this IApiClient apiClient)
- {
- return apiClient.GetPublicUsersAsync(CancellationToken.None);
- }
-
- public static Task<ItemsResult> GetItemsAsync(this IApiClient apiClient, ItemQuery query)
- {
- return apiClient.GetItemsAsync(query, CancellationToken.None);
- }
-
- public static Task<SyncDialogOptions> GetSyncOptions(this IApiClient apiClient, SyncJob job)
- {
- return apiClient.GetSyncOptions(new SyncJobRequest
- {
- Category = job.Category,
- ItemIds = job.RequestedItemIds,
- ParentId = job.ParentId,
- TargetId = job.TargetId,
- UserId = job.UserId
- });
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ApiHelpers.cs b/MediaBrowser.Model/ApiClient/ApiHelpers.cs
deleted file mode 100644
index 65b6495ab..000000000
--- a/MediaBrowser.Model/ApiClient/ApiHelpers.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public static class ApiHelpers
- {
- /// <summary>
- /// Gets the name of the slug.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>System.String.</returns>
- public static string GetSlugName(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException("name");
- }
-
- return name.Replace('/', '-').Replace('?', '-').Replace('&', '-');
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ConnectionMode.cs b/MediaBrowser.Model/ApiClient/ConnectionMode.cs
deleted file mode 100644
index 5dc224d95..000000000
--- a/MediaBrowser.Model/ApiClient/ConnectionMode.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.ApiClient
-{
- public enum ConnectionMode
- {
- Local = 1,
- Remote = 2,
- Manual = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/ConnectionOptions.cs b/MediaBrowser.Model/ApiClient/ConnectionOptions.cs
deleted file mode 100644
index e12676311..000000000
--- a/MediaBrowser.Model/ApiClient/ConnectionOptions.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ConnectionOptions
- {
- /// <summary>
- /// Gets or sets a value indicating whether [enable web socket].
- /// </summary>
- /// <value><c>true</c> if [enable web socket]; otherwise, <c>false</c>.</value>
- public bool EnableWebSocket { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [report capabilities].
- /// </summary>
- /// <value><c>true</c> if [report capabilities]; otherwise, <c>false</c>.</value>
- public bool ReportCapabilities { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [update date last accessed].
- /// </summary>
- /// <value><c>true</c> if [update date last accessed]; otherwise, <c>false</c>.</value>
- public bool UpdateDateLastAccessed { get; set; }
-
- public ConnectionOptions()
- {
- EnableWebSocket = true;
- ReportCapabilities = true;
- UpdateDateLastAccessed = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ConnectionResult.cs b/MediaBrowser.Model/ApiClient/ConnectionResult.cs
deleted file mode 100644
index 32a80d1a3..000000000
--- a/MediaBrowser.Model/ApiClient/ConnectionResult.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ConnectionResult
- {
- public ConnectionState State { get; set; }
- public List<ServerInfo> Servers { get; set; }
- public IApiClient ApiClient { get; set; }
- public ConnectUser ConnectUser { get; set; }
- public UserDto OfflineUser { get; set; }
-
- public ConnectionResult()
- {
- State = ConnectionState.Unavailable;
- Servers = new List<ServerInfo>();
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ConnectionState.cs b/MediaBrowser.Model/ApiClient/ConnectionState.cs
deleted file mode 100644
index 9b753c7bb..000000000
--- a/MediaBrowser.Model/ApiClient/ConnectionState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.ApiClient
-{
- public enum ConnectionState
- {
- Unavailable = 1,
- ServerSignIn = 2,
- SignedIn = 3,
- ServerSelection = 4,
- ConnectSignIn = 5,
- OfflineSignIn = 6,
- OfflineSignedIn = 7
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
deleted file mode 100644
index 2e9f57087..000000000
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ /dev/null
@@ -1,1427 +0,0 @@
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Playlists;
-using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Search;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Sync;
-using MediaBrowser.Model.System;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Interface IApiClient
- /// </summary>
- public interface IApiClient : IServerEvents, IDisposable
- {
- /// <summary>
- /// Occurs when [remote logged out].
- /// </summary>
- event EventHandler<GenericEventArgs<RemoteLogoutReason>> RemoteLoggedOut;
-
- /// <summary>
- /// Gets the API URL.
- /// </summary>
- /// <param name="handler">The handler.</param>
- /// <returns>System.String.</returns>
- string GetApiUrl(string handler);
-
- /// <summary>
- /// Gets the game system summaries async.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{List{GameSystemSummary}}.</returns>
- Task<List<GameSystemSummary>> GetGameSystemSummariesAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the async.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{``0}.</returns>
- Task<T> GetAsync<T>(string url, CancellationToken cancellationToken = default(CancellationToken))
- where T : class;
-
- /// <summary>
- /// Reports the capabilities.
- /// </summary>
- /// <param name="capabilities">The capabilities.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ReportCapabilities(ClientCapabilities capabilities, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Logouts this instance.
- /// </summary>
- /// <returns>Task.</returns>
- Task Logout();
-
- /// <summary>
- /// Gets the index of the game players.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{List{ItemIndex}}.</returns>
- Task<List<ItemIndex>> GetGamePlayerIndex(string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the index of the year.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="includeItemTypes">The include item types.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{List{ItemIndex}}.</returns>
- Task<List<ItemIndex>> GetYearIndex(string userId, string[] includeItemTypes, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the critic reviews.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="startIndex">The start index.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>Task{ItemReviewsResult}.</returns>
- Task<QueryResult<ItemReview>> GetCriticReviews(string itemId, CancellationToken cancellationToken = default(CancellationToken), int? startIndex = null, int? limit = null);
-
- /// <summary>
- /// Gets the theme songs async.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="itemId">The item id.</param>
- /// <param name="inheritFromParents">if set to <c>true</c> [inherit from parents].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ThemeMediaResult}.</returns>
- Task<ThemeMediaResult> GetThemeSongsAsync(string userId, string itemId, bool inheritFromParents, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the search hints async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{SearchHintResult}.</returns>
- Task<SearchHintResult> GetSearchHintsAsync(SearchQuery query);
-
- /// <summary>
- /// Gets the filters.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="parentId">The parent identifier.</param>
- /// <param name="mediaTypes">The media types.</param>
- /// <param name="itemTypes">The item types.</param>
- /// <returns>Task&lt;QueryFilters&gt;.</returns>
- Task<QueryFilters> GetFilters(string userId, string parentId, string[] mediaTypes, string[] itemTypes);
-
- /// <summary>
- /// Gets the theme videos async.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="itemId">The item id.</param>
- /// <param name="inheritFromParents">if set to <c>true</c> [inherit from parents].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ThemeMediaResult}.</returns>
- Task<ThemeMediaResult> GetThemeVideosAsync(string userId, string itemId, bool inheritFromParents, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets all theme media async.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="itemId">The item id.</param>
- /// <param name="inheritFromParents">if set to <c>true</c> [inherit from parents].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{AllThemeMediaResult}.</returns>
- Task<AllThemeMediaResult> GetAllThemeMediaAsync(string userId, string itemId, bool inheritFromParents, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Marks the notifications read.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="notificationIdList">The notification id list.</param>
- /// <param name="isRead">if set to <c>true</c> [is read].</param>
- /// <returns>Task.</returns>
- Task MarkNotificationsRead(string userId, IEnumerable<string> notificationIdList, bool isRead);
-
- /// <summary>
- /// Gets the notifications summary.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{NotificationsSummary}.</returns>
- Task<NotificationsSummary> GetNotificationsSummary(string userId);
-
- /// <summary>
- /// Gets the notifications async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{NotificationResult}.</returns>
- Task<NotificationResult> GetNotificationsAsync(NotificationQuery query);
-
- /// <summary>
- /// Gets an image stream based on a url
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- /// <exception cref="ArgumentNullException">url</exception>
- Task<Stream> GetImageStreamAsync(string url, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the stream.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;Stream&gt;.</returns>
- Task<Stream> GetStream(string url, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the response.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;HttpResponse&gt;.</returns>
- Task<HttpResponse> GetResponse(string url, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Updates the user configuration.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="configuration">The configuration.</param>
- /// <returns>Task.</returns>
- Task UpdateUserConfiguration(string userId, UserConfiguration configuration);
-
- /// <summary>
- /// Gets a BaseItem
- /// </summary>
- /// <param name="id">The id.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- /// <exception cref="ArgumentNullException">id</exception>
- Task<BaseItemDto> GetItemAsync(string id, string userId);
-
- /// <summary>
- /// Gets the latest items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;QueryResult&lt;BaseItemDto&gt;&gt;.</returns>
- Task<BaseItemDto[]> GetLatestItems(LatestItemsQuery query);
-
- /// <summary>
- /// Gets the intros async.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetIntrosAsync(string itemId, string userId);
-
- /// <summary>
- /// Gets a BaseItem
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<BaseItemDto> GetRootFolderAsync(string userId);
-
- /// <summary>
- /// Gets the additional parts.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Task{BaseItemDto[]}.</returns>
- Task<ItemsResult> GetAdditionalParts(string itemId, string userId);
-
- /// <summary>
- /// Gets the playback information.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;LiveMediaInfoResult&gt;.</returns>
- Task<PlaybackInfoResponse> GetPlaybackInfo(PlaybackInfoRequest request);
-
- /// <summary>
- /// Gets the users async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{UserDto[]}.</returns>
- Task<UserDto[]> GetUsersAsync(UserQuery query);
-
- /// <summary>
- /// Gets the public users async.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{UserDto[]}.</returns>
- Task<UserDto[]> GetPublicUsersAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets active client sessions.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{SessionInfoDto[]}.</returns>
- Task<SessionInfoDto[]> GetClientSessionsAsync(SessionQuery query);
-
- /// <summary>
- /// Gets the client session asynchronous.
- /// </summary>
- /// <returns>Task{SessionInfoDto}.</returns>
- Task<SessionInfoDto> GetCurrentSessionAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the item counts async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemCounts}.</returns>
- Task<ItemCounts> GetItemCountsAsync(ItemCountsQuery query);
-
- /// <summary>
- /// Gets the episodes asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetEpisodesAsync(EpisodeQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the seasons asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSeasonsAsync(SeasonQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- Task<PluginSecurityInfo> GetRegistrationInfo();
-
- /// <summary>
- /// Queries for items
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- /// <exception cref="ArgumentNullException">query</exception>
- Task<ItemsResult> GetItemsAsync(ItemQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the user views.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ItemsResult&gt;.</returns>
- Task<ItemsResult> GetUserViews(string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the instant mix from item asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;ItemsResult&gt;.</returns>
- Task<ItemsResult> GetInstantMixFromItemAsync(SimilarItemsQuery query);
-
- /// <summary>
- /// Gets the similar movies async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarItemsAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the people async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<ItemsResult> GetPeopleAsync(PersonsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the artists.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<ItemsResult> GetArtistsAsync(ArtistsQuery query);
-
- /// <summary>
- /// Gets the album artists asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetAlbumArtistsAsync(ArtistsQuery query);
-
- /// <summary>
- /// Gets the next up async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetNextUpEpisodesAsync(NextUpQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the upcoming episodes asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetUpcomingEpisodesAsync(UpcomingEpisodesQuery query);
-
- /// <summary>
- /// Gets a genre
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<BaseItemDto> GetGenreAsync(string name, string userId);
-
- /// <summary>
- /// Gets the genres async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetGenresAsync(ItemsByNameQuery query);
-
- /// <summary>
- /// Gets the studios async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetStudiosAsync(ItemsByNameQuery query);
-
- /// <summary>
- /// Gets the music genre async.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- Task<BaseItemDto> GetMusicGenreAsync(string name, string userId);
-
- /// <summary>
- /// Gets the game genre async.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- Task<BaseItemDto> GetGameGenreAsync(string name, string userId);
-
- /// <summary>
- /// Restarts the server.
- /// </summary>
- /// <returns>Task.</returns>
- Task RestartServerAsync();
-
- /// <summary>
- /// Gets the system status async.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SystemInfo}.</returns>
- Task<SystemInfo> GetSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the public system information asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;PublicSystemInfo&gt;.</returns>
- Task<PublicSystemInfo> GetPublicSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets a list of plugins installed on the server
- /// </summary>
- /// <returns>Task{PluginInfo[]}.</returns>
- Task<PluginInfo[]> GetInstalledPluginsAsync();
-
- /// <summary>
- /// Gets the current server configuration
- /// </summary>
- /// <returns>Task{ServerConfiguration}.</returns>
- Task<ServerConfiguration> GetServerConfigurationAsync();
-
- /// <summary>
- /// Gets the scheduled tasks.
- /// </summary>
- /// <returns>Task{TaskInfo[]}.</returns>
- Task<TaskInfo[]> GetScheduledTasksAsync();
-
- /// <summary>
- /// Gets the scheduled task async.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>Task{TaskInfo}.</returns>
- /// <exception cref="ArgumentNullException">id</exception>
- Task<TaskInfo> GetScheduledTaskAsync(string id);
-
- /// <summary>
- /// Gets a user by id
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>Task{UserDto}.</returns>
- /// <exception cref="ArgumentNullException">id</exception>
- Task<UserDto> GetUserAsync(string id);
-
- /// <summary>
- /// Gets the offline user asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task&lt;UserDto&gt;.</returns>
- Task<UserDto> GetOfflineUserAsync(string id);
-
- /// <summary>
- /// Gets the parental ratings async.
- /// </summary>
- /// <returns>Task{List{ParentalRating}}.</returns>
- Task<List<ParentalRating>> GetParentalRatingsAsync();
-
- /// <summary>
- /// Gets local trailers for an item
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="itemId">The item id.</param>
- /// <returns>Task{ItemsResult}.</returns>
- /// <exception cref="ArgumentNullException">query</exception>
- Task<BaseItemDto[]> GetLocalTrailersAsync(string userId, string itemId);
-
- /// <summary>
- /// Gets special features for an item
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="itemId">The item id.</param>
- /// <returns>Task{BaseItemDto[]}.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<BaseItemDto[]> GetSpecialFeaturesAsync(string userId, string itemId);
-
- /// <summary>
- /// Gets the cultures async.
- /// </summary>
- /// <returns>Task{CultureDto[]}.</returns>
- Task<CultureDto[]> GetCulturesAsync();
-
- /// <summary>
- /// Gets the countries async.
- /// </summary>
- /// <returns>Task{CountryInfo[]}.</returns>
- Task<CountryInfo[]> GetCountriesAsync();
-
- /// <summary>
- /// Marks the played async.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="datePlayed">The date played.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- Task<UserItemDataDto> MarkPlayedAsync(string itemId, string userId, DateTime? datePlayed);
-
- /// <summary>
- /// Marks the unplayed async.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- Task<UserItemDataDto> MarkUnplayedAsync(string itemId, string userId);
-
- /// <summary>
- /// Updates the favorite status async.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task<UserItemDataDto> UpdateFavoriteStatusAsync(string itemId, string userId, bool isFavorite);
-
- /// <summary>
- /// Reports to the server that the user has begun playing an item
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackStartAsync(PlaybackStartInfo info);
-
- /// <summary>
- /// Reports playback progress to the server
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackProgressAsync(PlaybackProgressInfo info);
-
- /// <summary>
- /// Reports to the server that the user has stopped playing an item
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
-
- /// <summary>
- /// Instructs another client to browse to a library item.
- /// </summary>
- /// <param name="sessionId">The session id.</param>
- /// <param name="itemId">The id of the item to browse to.</param>
- /// <param name="itemName">The name of the item to browse to.</param>
- /// <param name="itemType">The type of the item to browse to.</param>
- /// <returns>Task.</returns>
- Task SendBrowseCommandAsync(string sessionId, string itemId, string itemName, string itemType);
-
- /// <summary>
- /// Sends the playstate command async.
- /// </summary>
- /// <param name="sessionId">The session id.</param>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- Task SendPlaystateCommandAsync(string sessionId, PlaystateRequest request);
-
- /// <summary>
- /// Sends the play command async.
- /// </summary>
- /// <param name="sessionId">The session id.</param>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">sessionId
- /// or
- /// request</exception>
- Task SendPlayCommandAsync(string sessionId, PlayRequest request);
-
- /// <summary>
- /// Sends the command asynchronous.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="command">The command.</param>
- /// <returns>Task.</returns>
- Task SendCommandAsync(string sessionId, GeneralCommand command);
-
- /// <summary>
- /// Sends the string.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="text">The text.</param>
- /// <returns>Task.</returns>
- Task SendString(string sessionId, string text);
-
- /// <summary>
- /// Sets the volume.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="volume">The volume.</param>
- /// <returns>Task.</returns>
- Task SetVolume(string sessionId, int volume);
-
- /// <summary>
- /// Stops the transcoding processes.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="streamId">The stream identifier.</param>
- /// <returns>Task.</returns>
- Task StopTranscodingProcesses(string deviceId, string streamId);
-
- /// <summary>
- /// Sets the index of the audio stream.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="index">The index.</param>
- /// <returns>Task.</returns>
- Task SetAudioStreamIndex(string sessionId, int index);
-
- /// <summary>
- /// Sets the index of the subtitle stream.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="index">The index.</param>
- /// <returns>Task.</returns>
- Task SetSubtitleStreamIndex(string sessionId, int? index);
-
- /// <summary>
- /// Instructs the client to display a message to the user
- /// </summary>
- /// <param name="sessionId">The session id.</param>
- /// <param name="command">The command.</param>
- /// <returns>Task.</returns>
- Task SendMessageCommandAsync(string sessionId, MessageCommand command);
-
- /// <summary>
- /// Clears a user's rating for an item
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{UserItemDataDto}.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task<UserItemDataDto> ClearUserItemRatingAsync(string itemId, string userId);
-
- /// <summary>
- /// Updates a user's rating for an item, based on likes or dislikes
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="likes">if set to <c>true</c> [likes].</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- Task<UserItemDataDto> UpdateUserItemRatingAsync(string itemId, string userId, bool likes);
-
- /// <summary>
- /// Authenticates a user and returns the result
- /// </summary>
- /// <param name="username">The username.</param>
- /// <param name="password">The password.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- Task<AuthenticationResult> AuthenticateUserAsync(string username,
- string password);
-
- /// <summary>
- /// Updates the server configuration async.
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">configuration</exception>
- Task UpdateServerConfigurationAsync(ServerConfiguration configuration);
-
- /// <summary>
- /// Updates the scheduled task triggers.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <param name="triggers">The triggers.</param>
- /// <returns>Task{RequestResult}.</returns>
- /// <exception cref="ArgumentNullException">id</exception>
- Task UpdateScheduledTaskTriggersAsync(string id, TaskTriggerInfo[] triggers);
-
- /// <summary>
- /// Gets the display preferences.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="client">The client.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{BaseItemDto}.</returns>
- Task<DisplayPreferences> GetDisplayPreferencesAsync(string id, string userId, string client, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Updates display preferences for a user
- /// </summary>
- /// <param name="displayPreferences">The display preferences.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="client">The client.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- /// <exception cref="System.ArgumentNullException">userId</exception>
- Task UpdateDisplayPreferencesAsync(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Posts a set of data to a url, and deserializes the return stream into T
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="url">The URL.</param>
- /// <param name="args">The args.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{``0}.</returns>
- Task<T> PostAsync<T>(string url, Dictionary<string, string> args, CancellationToken cancellationToken = default(CancellationToken))
- where T : class;
-
- /// <summary>
- /// This is a helper around getting a stream from the server that contains serialized data
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <returns>Task{Stream}.</returns>
- Task<Stream> GetSerializedStreamAsync(string url);
-
- /// <summary>
- /// Gets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- IJsonSerializer JsonSerializer { get; set; }
-
- /// <summary>
- /// Gets or sets the server address
- /// </summary>
- /// <value>The server address.</value>
- string ServerAddress { get; }
-
- /// <summary>
- /// Gets or sets the type of the client.
- /// </summary>
- /// <value>The type of the client.</value>
- string ClientName { get; set; }
-
- /// <summary>
- /// Gets the device.
- /// </summary>
- /// <value>The device.</value>
- IDevice Device { get; }
-
- /// <summary>
- /// Gets or sets the name of the device.
- /// </summary>
- /// <value>The name of the device.</value>
- string DeviceName { get; }
-
- /// <summary>
- /// Gets or sets the device id.
- /// </summary>
- /// <value>The device id.</value>
- string DeviceId { get; }
-
- /// <summary>
- /// Gets or sets the current user id.
- /// </summary>
- /// <value>The current user id.</value>
- string CurrentUserId { get; }
-
- /// <summary>
- /// Gets the access token.
- /// </summary>
- /// <value>The access token.</value>
- string AccessToken { get; }
-
- /// <summary>
- /// Sets the authentication information.
- /// </summary>
- /// <param name="accessToken">The access token.</param>
- /// <param name="userId">The user identifier.</param>
- void SetAuthenticationInfo(string accessToken, string userId);
-
- /// <summary>
- /// Sets the authentication information.
- /// </summary>
- /// <param name="accessToken">The access token.</param>
- void SetAuthenticationInfo(string accessToken);
-
- /// <summary>
- /// Clears the authentication information.
- /// </summary>
- void ClearAuthenticationInfo();
-
- /// <summary>
- /// Changes the server location.
- /// </summary>
- /// <param name="address">The address.</param>
- /// <param name="keepExistingAuth">if set to <c>true</c> [keep existing authentication].</param>
- void ChangeServerLocation(string address, bool keepExistingAuth = false);
-
- /// <summary>
- /// Starts the receiving synchronize job updates.
- /// </summary>
- /// <param name="intervalMs">The interval ms.</param>
- /// <param name="jobId">The job identifier.</param>
- /// <returns>Task.</returns>
- Task StartReceivingSyncJobUpdates(int intervalMs, string jobId);
-
- /// <summary>
- /// Stops the receiving synchronize job updates.
- /// </summary>
- /// <returns>Task.</returns>
- Task StopReceivingSyncJobUpdates();
-
- /// <summary>
- /// Starts the receiving synchronize jobs updates.
- /// </summary>
- /// <param name="intervalMs">The interval ms.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="targetId">The target identifier.</param>
- /// <returns>Task.</returns>
- Task StartReceivingSyncJobsUpdates(int intervalMs, string userId, string targetId);
-
- /// <summary>
- /// Stops the receiving synchronize jobs updates.
- /// </summary>
- /// <returns>Task.</returns>
- Task StopReceivingSyncJobsUpdates();
-
- /// <summary>
- /// Starts the receiving session updates.
- /// </summary>
- /// <param name="intervalMs">The interval ms.</param>
- /// <returns>Task.</returns>
- Task StartReceivingSessionUpdates(int intervalMs);
-
- /// <summary>
- /// Stops the receiving session updates.
- /// </summary>
- /// <returns>Task.</returns>
- Task StopReceivingSessionUpdates();
-
- /// <summary>
- /// Gets the image URL.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- string GetImageUrl(BaseItemDto item, ImageOptions options);
-
- /// <summary>
- /// Gets the image URL.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetImageUrl(ChannelInfoDto item, ImageOptions options);
-
- /// <summary>
- /// Gets the subtitle URL.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetSubtitleUrl(SubtitleDownloadOptions options);
-
- /// <summary>
- /// Gets an image url that can be used to download an image from the api
- /// </summary>
- /// <param name="itemId">The Id of the item</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">itemId</exception>
- string GetImageUrl(string itemId, ImageOptions options);
-
- /// <summary>
- /// Gets the user image URL.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- string GetUserImageUrl(UserDto user, ImageOptions options);
-
- /// <summary>
- /// Gets an image url that can be used to download an image from the api
- /// </summary>
- /// <param name="userId">The Id of the user</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">userId</exception>
- string GetUserImageUrl(string userId, ImageOptions options);
-
- /// <summary>
- /// Gets the person image URL.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- string GetPersonImageUrl(BaseItemPerson item, ImageOptions options);
-
- /// <summary>
- /// Gets an image url that can be used to download an image from the api
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">name</exception>
- string GetGenreImageUrl(string name, ImageOptions options);
-
- /// <summary>
- /// Gets the music genre image URL.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetMusicGenreImageUrl(string name, ImageOptions options);
-
- /// <summary>
- /// Gets the game genre image URL.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetGameGenreImageUrl(string name, ImageOptions options);
-
- /// <summary>
- /// This is a helper to get a list of backdrop url's from a given ApiBaseItemWrapper. If the actual item does not have any backdrops it will return backdrops from the first parent that does.
- /// </summary>
- /// <param name="item">A given item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String[][].</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- string[] GetBackdropImageUrls(BaseItemDto item, ImageOptions options);
-
- /// <summary>
- /// This is a helper to get the logo image url from a given ApiBaseItemWrapper. If the actual item does not have a logo, it will return the logo from the first parent that does, or null.
- /// </summary>
- /// <param name="item">A given item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- string GetLogoImageUrl(BaseItemDto item, ImageOptions options);
-
- /// <summary>
- /// Gets the art image URL.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetArtImageUrl(BaseItemDto item, ImageOptions options);
-
- /// <summary>
- /// Gets the thumb image URL.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- string GetThumbImageUrl(BaseItemDto item, ImageOptions options);
-
- /// <summary>
- /// Gets the live tv information asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{LiveTvInfo}.</returns>
- Task<LiveTvInfo> GetLiveTvInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv channels asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{LiveTvInfo}.</returns>
- Task<QueryResult<ChannelInfoDto>> GetLiveTvChannelsAsync(LiveTvChannelQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv channel asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ChannelInfoDto}.</returns>
- Task<ChannelInfoDto> GetLiveTvChannelAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv recordings asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{RecordingInfoDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetLiveTvRecordingsAsync(RecordingQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv recording asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{RecordingInfoDto}.</returns>
- Task<BaseItemDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv recording groups asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{RecordingGroupDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetLiveTvRecordingGroupsAsync(RecordingGroupQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv recording group asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{RecordingGroupDto}.</returns>
- Task<BaseItemDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv timers asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{TimerInfoDto}}.</returns>
- Task<QueryResult<TimerInfoDto>> GetLiveTvTimersAsync(TimerQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv programs asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv program asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ProgramInfoDto}.</returns>
- Task<BaseItemDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the recommended live tv programs asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Creates the live tv timer asynchronous.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateLiveTvTimerAsync(BaseTimerInfoDto timer, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Updates the live tv timer asynchronous.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Creates the live tv series timer asynchronous.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Updates the live tv series timer asynchronous.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv timer asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{TimerInfoDto}.</returns>
- Task<TimerInfoDto> GetLiveTvTimerAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv series timers asynchronous.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{SeriesTimerInfoDto}}.</returns>
- Task<QueryResult<SeriesTimerInfoDto>> GetLiveTvSeriesTimersAsync(SeriesTimerQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv series timer asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SeriesTimerInfoDto}.</returns>
- Task<SeriesTimerInfoDto> GetLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Cancels the live tv timer asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CancelLiveTvTimerAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Cancels the live tv series timer asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the default timer information.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SeriesTimerInfoDto}.</returns>
- Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the live tv guide information.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{GuideInfo}.</returns>
- Task<GuideInfo> GetLiveTvGuideInfo(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the default timer information.
- /// </summary>
- /// <param name="programId">The program identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SeriesTimerInfoDto}.</returns>
- Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(string programId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the channel features.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ChannelFeatures}.</returns>
- Task<ChannelFeatures> GetChannelFeatures(string channelId, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the channel items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the channels.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the latest channel items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetLatestChannelItems(AllChannelMediaQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Creates the playlist.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;PlaylistCreationResult&gt;.</returns>
- Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest request);
-
- /// <summary>
- /// Adds to playlist.
- /// </summary>
- /// <param name="playlistId">The playlist identifier.</param>
- /// <param name="itemIds">The item ids.</param>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Task.</returns>
- Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId);
-
- /// <summary>
- /// Removes from playlist.
- /// </summary>
- /// <param name="playlistId">The playlist identifier.</param>
- /// <param name="entryIds">The entry ids.</param>
- /// <returns>Task.</returns>
- Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds);
-
- /// <summary>
- /// Gets the playlist items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;QueryResult&lt;BaseItemDto&gt;&gt;.</returns>
- Task<QueryResult<BaseItemDto>> GetPlaylistItems(PlaylistItemQuery query);
-
- /// <summary>
- /// Sends the context message asynchronous.
- /// </summary>
- /// <param name="itemType">Type of the item.</param>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="itemName">Name of the item.</param>
- /// <param name="context">The context.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendContextMessageAsync(string itemType, string itemId, string itemName, string context,
- CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the content upload history.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <returns>Task&lt;ContentUploadHistory&gt;.</returns>
- Task<ContentUploadHistory> GetContentUploadHistory(string deviceId);
-
- /// <summary>
- /// Uploads the file.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <param name="file">The file.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UploadFile(Stream stream,
- LocalFileInfo file,
- CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the devices options options.
- /// </summary>
- /// <returns>Task&lt;DevicesOptions&gt;.</returns>
- Task<DevicesOptions> GetDevicesOptions();
-
- /// <summary>
- /// Updates the item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- Task UpdateItem(BaseItemDto item);
-
- /// <summary>
- /// Creates the synchronize job.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;SyncJob&gt;.</returns>
- Task<SyncJob> CreateSyncJob(SyncJobRequest request);
-
- /// <summary>
- /// Updates the synchronize job.
- /// </summary>
- /// <param name="job">The job.</param>
- /// <returns>Task.</returns>
- Task UpdateSyncJob(SyncJob job);
-
- /// <summary>
- /// Gets the synchronize jobs.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;QueryResult&lt;SyncJob&gt;&gt;.</returns>
- Task<QueryResult<SyncJob>> GetSyncJobs(SyncJobQuery query);
-
- /// <summary>
- /// Gets the synchronize job items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;QueryResult&lt;SyncJobItem&gt;&gt;.</returns>
- Task<QueryResult<SyncJobItem>> GetSyncJobItems(SyncJobItemQuery query);
-
- /// <summary>
- /// Reports the synchronize job item transferred.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task ReportSyncJobItemTransferred(string id);
-
- /// <summary>
- /// Gets the synchronize job item file.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;Stream&gt;.</returns>
- Task<Stream> GetSyncJobItemFile(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the synchronize job item additional file.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="name">The name.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;Stream&gt;.</returns>
- Task<Stream> GetSyncJobItemAdditionalFile(string id, string name, CancellationToken cancellationToken);
-
- /// <summary>
- /// Opens the web socket.
- /// </summary>
- /// <param name="webSocketFactory">The web socket factory.</param>
- /// <param name="keepAliveTimerMs">The keep alive timer ms.</param>
- void OpenWebSocket(Func<IClientWebSocket> webSocketFactory, int keepAliveTimerMs = 60000);
-
- /// <summary>
- /// Reports the offline actions.
- /// </summary>
- /// <param name="actions">The actions.</param>
- /// <returns>Task.</returns>
- Task ReportOfflineActions(List<UserAction> actions);
-
- /// <summary>
- /// Gets the ready synchronize items.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- /// <returns>List&lt;SyncedItem&gt;.</returns>
- Task<List<SyncedItem>> GetReadySyncItems(string targetId);
-
- /// <summary>
- /// Synchronizes the data.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;SyncDataResponse&gt;.</returns>
- Task<SyncDataResponse> SyncData(SyncDataRequest request);
- /// <summary>
- /// Gets the synchronize job item file URL.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>System.String.</returns>
- string GetSyncJobItemFileUrl(string id);
- /// <summary>
- /// Marks the synchronize job item for removal.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task MarkSyncJobItemForRemoval(string id);
- /// <summary>
- /// Unmarks the synchronize job item for removal.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task UnmarkSyncJobItemForRemoval(string id);
- /// <summary>
- /// Queues the failed synchronize job item for retry.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task QueueFailedSyncJobItemForRetry(string id);
- /// <summary>
- /// Cancels the synchronize job.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelSyncJob(string id);
- /// <summary>
- /// Cancels the synchronize job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelSyncJobItem(string id);
- /// <summary>
- /// Enables the cancelled synchronize job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task EnableCancelledSyncJobItem(string id);
- /// <summary>
- /// Gets the synchronize options.
- /// </summary>
- /// <param name="jobInfo">The job information.</param>
- /// <returns>Task&lt;SyncOptions&gt;.</returns>
- Task<SyncDialogOptions> GetSyncOptions(SyncJobRequest jobInfo);
- /// <summary>
- /// Gets the synchronize options.
- /// </summary>
- /// <param name="jobInfo">The job information.</param>
- /// <returns>Task&lt;SyncDialogOptions&gt;.</returns>
- Task<SyncDialogOptions> GetSyncOptions(SyncJob jobInfo);
- /// <summary>
- /// Gets the movie recommendations.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task&lt;List&lt;RecommendationDto&gt;&gt;.</returns>
- Task<List<RecommendationDto>> GetMovieRecommendations(MovieRecommendationQuery query);
- /// <summary>
- /// Opens the live stream.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;LiveStreamResponse&gt;.</returns>
- Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken);
- /// <summary>
- /// Cancels the synchronize library items.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- /// <param name="itemIds">The item ids.</param>
- /// <returns>Task.</returns>
- Task CancelSyncLibraryItems(string targetId, IEnumerable<string> itemIds);
- /// <summary>
- /// Gets the supported bitrate.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;System.Int32&gt;.</returns>
- Task<int> DetectMaxBitrate(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the end point information.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>System.Threading.Tasks.Task&lt;MediaBrowser.Model.Net.EndPointInfo&gt;.</returns>
- Task<EndPointInfo> GetEndPointInfo(CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IClientWebSocket.cs b/MediaBrowser.Model/ApiClient/IClientWebSocket.cs
deleted file mode 100644
index ca3a761d4..000000000
--- a/MediaBrowser.Model/ApiClient/IClientWebSocket.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using MediaBrowser.Model.Net;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Interface IClientWebSocket
- /// </summary>
- public interface IClientWebSocket : IDisposable
- {
- /// <summary>
- /// Occurs when [closed].
- /// </summary>
- event EventHandler Closed;
-
- /// <summary>
- /// Gets or sets the state.
- /// </summary>
- /// <value>The state.</value>
- WebSocketState State { get; }
-
- /// <summary>
- /// Connects the async.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ConnectAsync(string url, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
- Action<byte[]> OnReceiveBytes { get; set; }
-
- /// <summary>
- /// Gets or sets the on receive.
- /// </summary>
- /// <value>The on receive.</value>
- Action<string> OnReceive { get; set; }
-
- /// <summary>
- /// Sends the async.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="type">The type.</param>
- /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs
deleted file mode 100644
index 083f230bc..000000000
--- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public interface IConnectionManager
- {
- /// <summary>
- /// Occurs when [connected].
- /// </summary>
- event EventHandler<GenericEventArgs<ConnectionResult>> Connected;
- /// <summary>
- /// Occurs when [local user sign in].
- /// </summary>
- event EventHandler<GenericEventArgs<UserDto>> LocalUserSignIn;
- /// <summary>
- /// Occurs when [connect user sign in].
- /// </summary>
- event EventHandler<GenericEventArgs<ConnectUser>> ConnectUserSignIn;
- /// <summary>
- /// Occurs when [local user sign out].
- /// </summary>
- event EventHandler<GenericEventArgs<IApiClient>> LocalUserSignOut;
- /// <summary>
- /// Occurs when [connect user sign out].
- /// </summary>
- event EventHandler<EventArgs> ConnectUserSignOut;
- /// <summary>
- /// Occurs when [remote logged out].
- /// </summary>
- event EventHandler<EventArgs> RemoteLoggedOut;
-
- /// <summary>
- /// Gets the device.
- /// </summary>
- /// <value>The device.</value>
- IDevice Device { get; }
-
- /// <summary>
- /// Gets the connect user.
- /// </summary>
- /// <value>The connect user.</value>
- ConnectUser ConnectUser { get; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [save local credentials].
- /// </summary>
- /// <value><c>true</c> if [save local credentials]; otherwise, <c>false</c>.</value>
- bool SaveLocalCredentials { get; set; }
-
- /// <summary>
- /// Gets the client capabilities.
- /// </summary>
- /// <value>The client capabilities.</value>
- ClientCapabilities ClientCapabilities { get; }
-
- /// <summary>
- /// Gets the API client.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IApiClient.</returns>
- IApiClient GetApiClient(IHasServerId item);
-
- /// <summary>
- /// Gets the API client.
- /// </summary>
- /// <param name="serverId">The server identifier.</param>
- /// <returns>IApiClient.</returns>
- IApiClient GetApiClient(string serverId);
-
- /// <summary>
- /// Connects the specified cancellation token.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task<ConnectionResult> Connect(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Connects the specified API client.
- /// </summary>
- /// <param name="apiClient">The API client.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task<ConnectionResult> Connect(IApiClient apiClient, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Connects the specified server.
- /// </summary>
- /// <param name="server">The server.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task<ConnectionResult> Connect(ServerInfo server, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Connects the specified server.
- /// </summary>
- /// <param name="server">The server.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task<ConnectionResult> Connect(ServerInfo server, ConnectionOptions options, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Connects the specified server.
- /// </summary>
- /// <param name="address">The address.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task<ConnectionResult> Connect(string address, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Logouts this instance.
- /// </summary>
- /// <returns>Task&lt;ConnectionResult&gt;.</returns>
- Task Logout();
-
- /// <summary>
- /// Logins to connect.
- /// </summary>
- /// <returns>Task.</returns>
- Task LoginToConnect(string username, string password);
-
- /// <summary>
- /// Gets the active api client instance
- /// </summary>
- IApiClient CurrentApiClient { get; }
-
- /// <summary>
- /// Creates the pin.
- /// </summary>
- /// <returns>Task&lt;PinCreationResult&gt;.</returns>
- Task<PinCreationResult> CreatePin();
-
- /// <summary>
- /// Gets the pin status.
- /// </summary>
- /// <param name="pin">The pin.</param>
- /// <returns>Task&lt;PinStatusResult&gt;.</returns>
- Task<PinStatusResult> GetPinStatus(PinCreationResult pin);
-
- /// <summary>
- /// Exchanges the pin.
- /// </summary>
- /// <param name="pin">The pin.</param>
- /// <returns>Task.</returns>
- Task ExchangePin(PinCreationResult pin);
-
- /// <summary>
- /// Gets the server information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task&lt;ServerInfo&gt;.</returns>
- Task<ServerInfo> GetServerInfo(string id);
-
- /// <summary>
- /// Gets the available servers.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- Task<List<ServerInfo>> GetAvailableServers(CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Authenticates an offline user with their password
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="password">The password.</param>
- /// <param name="rememberCredentials">if set to <c>true</c> [remember credentials].</param>
- /// <returns>Task.</returns>
- Task AuthenticateOffline(UserDto user, string password, bool rememberCredentials);
-
- /// <summary>
- /// Gets the offline users.
- /// </summary>
- /// <returns>Task&lt;List&lt;UserDto&gt;&gt;.</returns>
- Task<List<UserDto>> GetOfflineUsers();
-
- /// <summary>
- /// Signups for connect.
- /// </summary>
- /// <param name="email">The email.</param>
- /// <param name="username">The username.</param>
- /// <param name="password">The password.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<ConnectSignupResponse> SignupForConnect(string email, string username, string password, CancellationToken cancellationToken = default(CancellationToken));
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/IDevice.cs b/MediaBrowser.Model/ApiClient/IDevice.cs
deleted file mode 100644
index 7b67122fb..000000000
--- a/MediaBrowser.Model/ApiClient/IDevice.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Model.Devices;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public interface IDevice
- {
- /// <summary>
- /// Occurs when [resume from sleep].
- /// </summary>
- event EventHandler<EventArgs> ResumeFromSleep;
- /// <summary>
- /// Gets the name of the device.
- /// </summary>
- /// <value>The name of the device.</value>
- string DeviceName { get; }
- /// <summary>
- /// Gets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- string DeviceId { get; }
- /// <summary>
- /// Gets the local images.
- /// </summary>
- /// <returns>IEnumerable&lt;LocalFileInfo&gt;.</returns>
- Task<IEnumerable<LocalFileInfo>> GetLocalPhotos();
- /// <summary>
- /// Gets the local videos.
- /// </summary>
- /// <returns>IEnumerable&lt;LocalFileInfo&gt;.</returns>
- Task<IEnumerable<LocalFileInfo>> GetLocalVideos();
- /// <summary>
- /// Uploads the file.
- /// </summary>
- /// <param name="file">The file.</param>
- /// <param name="apiClient">The API client.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UploadFile(LocalFileInfo file, IApiClient apiClient, CancellationToken cancellationToken = default(CancellationToken));
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/IServerEvents.cs b/MediaBrowser.Model/ApiClient/IServerEvents.cs
deleted file mode 100644
index ae2d5d323..000000000
--- a/MediaBrowser.Model/ApiClient/IServerEvents.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Sync;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Interface IServerEvents
- /// </summary>
- public interface IServerEvents
- {
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- event EventHandler<GenericEventArgs<string>> UserDeleted;
- /// <summary>
- /// Occurs when [scheduled task ended].
- /// </summary>
- event EventHandler<GenericEventArgs<TaskResult>> ScheduledTaskEnded;
- /// <summary>
- /// Occurs when [package installing].
- /// </summary>
- event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstalling;
- /// <summary>
- /// Occurs when [package installation failed].
- /// </summary>
- event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationFailed;
- /// <summary>
- /// Occurs when [package installation completed].
- /// </summary>
- event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCompleted;
- /// <summary>
- /// Occurs when [package installation cancelled].
- /// </summary>
- event EventHandler<GenericEventArgs<InstallationInfo>> PackageInstallationCancelled;
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- event EventHandler<GenericEventArgs<UserDto>> UserUpdated;
- /// <summary>
- /// Occurs when [plugin uninstalled].
- /// </summary>
- event EventHandler<GenericEventArgs<PluginInfo>> PluginUninstalled;
- /// <summary>
- /// Occurs when [library changed].
- /// </summary>
- event EventHandler<GenericEventArgs<LibraryUpdateInfo>> LibraryChanged;
- /// <summary>
- /// Occurs when [browse command].
- /// </summary>
- event EventHandler<GenericEventArgs<BrowseRequest>> BrowseCommand;
- /// <summary>
- /// Occurs when [play command].
- /// </summary>
- event EventHandler<GenericEventArgs<PlayRequest>> PlayCommand;
- /// <summary>
- /// Occurs when [playstate command].
- /// </summary>
- event EventHandler<GenericEventArgs<PlaystateRequest>> PlaystateCommand;
- /// <summary>
- /// Occurs when [message command].
- /// </summary>
- event EventHandler<GenericEventArgs<MessageCommand>> MessageCommand;
- /// <summary>
- /// Occurs when [system command].
- /// </summary>
- event EventHandler<GenericEventArgs<GeneralCommandEventArgs>> GeneralCommand;
- /// <summary>
- /// Occurs when [notification added].
- /// </summary>
- event EventHandler<EventArgs> NotificationAdded;
- /// <summary>
- /// Occurs when [notification updated].
- /// </summary>
- event EventHandler<EventArgs> NotificationUpdated;
- /// <summary>
- /// Occurs when [notifications marked read].
- /// </summary>
- event EventHandler<EventArgs> NotificationsMarkedRead;
- /// <summary>
- /// Occurs when [server restarting].
- /// </summary>
- event EventHandler<EventArgs> ServerRestarting;
- /// <summary>
- /// Occurs when [server shutting down].
- /// </summary>
- event EventHandler<EventArgs> ServerShuttingDown;
- /// <summary>
- /// Occurs when [send text command].
- /// </summary>
- event EventHandler<GenericEventArgs<string>> SendStringCommand;
- /// <summary>
- /// Occurs when [set volume command].
- /// </summary>
- event EventHandler<GenericEventArgs<int>> SetVolumeCommand;
- /// <summary>
- /// Occurs when [set audio stream index command].
- /// </summary>
- event EventHandler<GenericEventArgs<int>> SetAudioStreamIndexCommand;
- /// <summary>
- /// Occurs when [set video stream index command].
- /// </summary>
- event EventHandler<GenericEventArgs<int>> SetSubtitleStreamIndexCommand;
- /// <summary>
- /// Occurs when [sessions updated].
- /// </summary>
- event EventHandler<GenericEventArgs<SessionUpdatesEventArgs>> SessionsUpdated;
- /// <summary>
- /// Occurs when [restart required].
- /// </summary>
- event EventHandler<EventArgs> RestartRequired;
- /// <summary>
- /// Occurs when [user data changed].
- /// </summary>
- event EventHandler<GenericEventArgs<UserDataChangeInfo>> UserDataChanged;
- /// <summary>
- /// Occurs when [playback start].
- /// </summary>
- event EventHandler<GenericEventArgs<SessionInfoDto>> PlaybackStart;
- /// <summary>
- /// Occurs when [playback stopped].
- /// </summary>
- event EventHandler<GenericEventArgs<SessionInfoDto>> PlaybackStopped;
- /// <summary>
- /// Occurs when [session ended].
- /// </summary>
- event EventHandler<GenericEventArgs<SessionInfoDto>> SessionEnded;
- /// <summary>
- /// Occurs when [synchronize job created].
- /// </summary>
- event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
- /// <summary>
- /// Occurs when [synchronize job cancelled].
- /// </summary>
- event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
- /// <summary>
- /// Occurs when [synchronize jobs updated].
- /// </summary>
- event EventHandler<GenericEventArgs<List<SyncJob>>> SyncJobsUpdated;
- /// <summary>
- /// Occurs when [synchronize job updated].
- /// </summary>
- event EventHandler<GenericEventArgs<CompleteSyncJobInfo>> SyncJobUpdated;
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/NetworkStatus.cs b/MediaBrowser.Model/ApiClient/NetworkStatus.cs
deleted file mode 100644
index 715087607..000000000
--- a/MediaBrowser.Model/ApiClient/NetworkStatus.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class NetworkStatus
- {
- /// <summary>
- /// Gets or sets a value indicating whether this instance is network available.
- /// </summary>
- /// <value><c>true</c> if this instance is network available; otherwise, <c>false</c>.</value>
- public bool IsNetworkAvailable { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is local network available.
- /// </summary>
- /// <value><c>null</c> if [is local network available] contains no value, <c>true</c> if [is local network available]; otherwise, <c>false</c>.</value>
- public bool? IsLocalNetworkAvailable { get; set; }
- /// <summary>
- /// Gets the is any local network available.
- /// </summary>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public bool GetIsAnyLocalNetworkAvailable()
- {
- if (!IsLocalNetworkAvailable.HasValue)
- {
- return IsNetworkAvailable;
- }
-
- return IsLocalNetworkAvailable.Value;
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/RemoteLogoutReason.cs b/MediaBrowser.Model/ApiClient/RemoteLogoutReason.cs
deleted file mode 100644
index 237949c69..000000000
--- a/MediaBrowser.Model/ApiClient/RemoteLogoutReason.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.ApiClient
-{
- public enum RemoteLogoutReason
- {
- GeneralAccesError = 0,
- ParentalControlRestriction = 1
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs
deleted file mode 100644
index ddeb7e546..000000000
--- a/MediaBrowser.Model/ApiClient/ServerCredentials.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ServerCredentials
- {
- public List<ServerInfo> Servers { get; set; }
-
- public string ConnectUserId { get; set; }
- public string ConnectAccessToken { get; set; }
-
- public ServerCredentials()
- {
- Servers = new List<ServerInfo>();
- }
-
- public void AddOrUpdateServer(ServerInfo server)
- {
- if (server == null)
- {
- throw new ArgumentNullException("server");
- }
-
- // Clone the existing list of servers
- var list = new List<ServerInfo>();
- foreach (ServerInfo serverInfo in Servers)
- {
- list.Add(serverInfo);
- }
-
- var index = FindIndex(list, server.Id);
-
- if (index != -1)
- {
- var existing = list[index];
-
- // Take the most recent DateLastAccessed
- if (server.DateLastAccessed > existing.DateLastAccessed)
- {
- existing.DateLastAccessed = server.DateLastAccessed;
- }
-
- existing.UserLinkType = server.UserLinkType;
-
- if (!string.IsNullOrEmpty(server.AccessToken))
- {
- existing.AccessToken = server.AccessToken;
- existing.UserId = server.UserId;
- }
- if (!string.IsNullOrEmpty(server.ExchangeToken))
- {
- existing.ExchangeToken = server.ExchangeToken;
- }
- if (!string.IsNullOrEmpty(server.RemoteAddress))
- {
- existing.RemoteAddress = server.RemoteAddress;
- }
- if (!string.IsNullOrEmpty(server.ConnectServerId))
- {
- existing.ConnectServerId = server.ConnectServerId;
- }
- if (!string.IsNullOrEmpty(server.LocalAddress))
- {
- existing.LocalAddress = server.LocalAddress;
- }
- if (!string.IsNullOrEmpty(server.ManualAddress))
- {
- existing.ManualAddress = server.ManualAddress;
- }
- if (!string.IsNullOrEmpty(server.Name))
- {
- existing.Name = server.Name;
- }
- if (server.WakeOnLanInfos != null && server.WakeOnLanInfos.Count > 0)
- {
- existing.WakeOnLanInfos = new List<WakeOnLanInfo>();
- foreach (WakeOnLanInfo info in server.WakeOnLanInfos)
- {
- existing.WakeOnLanInfos.Add(info);
- }
- }
- if (server.LastConnectionMode.HasValue)
- {
- existing.LastConnectionMode = server.LastConnectionMode;
- }
- foreach (ServerUserInfo user in server.Users)
- {
- existing.AddOrUpdate(user);
- }
- }
- else
- {
- list.Add(server);
- }
-
- Servers = list;
- }
-
- private int FindIndex(List<ServerInfo> servers, string id)
- {
- var index = 0;
-
- foreach (ServerInfo server in servers)
- {
- if (StringHelper.EqualsIgnoreCase(id, server.Id))
- {
- return index;
- }
-
- index++;
- }
-
- return -1;
- }
-
- public ServerInfo GetServer(string id)
- {
- foreach (ServerInfo server in Servers)
- {
- if (StringHelper.EqualsIgnoreCase(id, server.Id))
- {
- return server;
- }
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ServerInfo.cs b/MediaBrowser.Model/ApiClient/ServerInfo.cs
deleted file mode 100644
index 48995e80a..000000000
--- a/MediaBrowser.Model/ApiClient/ServerInfo.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.System;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ServerInfo
- {
- public List<ServerUserInfo> Users { get; set; }
-
- public String Name { get; set; }
- public String Id { get; set; }
- public String ConnectServerId { get; set; }
- public String LocalAddress { get; set; }
- public String RemoteAddress { get; set; }
- public String ManualAddress { get; set; }
- public String UserId { get; set; }
- public String AccessToken { get; set; }
- public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
- public DateTime DateLastAccessed { get; set; }
- public String ExchangeToken { get; set; }
- public UserLinkType? UserLinkType { get; set; }
- public ConnectionMode? LastConnectionMode { get; set; }
-
- public ServerInfo()
- {
- WakeOnLanInfos = new List<WakeOnLanInfo>();
- Users = new List<ServerUserInfo>();
- }
-
- public void ImportInfo(PublicSystemInfo systemInfo)
- {
- Name = systemInfo.ServerName;
- Id = systemInfo.Id;
-
- if (!string.IsNullOrEmpty(systemInfo.LocalAddress))
- {
- LocalAddress = systemInfo.LocalAddress;
- }
-
- if (!string.IsNullOrEmpty(systemInfo.WanAddress))
- {
- RemoteAddress = systemInfo.WanAddress;
- }
-
- var fullSystemInfo = systemInfo as SystemInfo;
-
- if (fullSystemInfo != null)
- {
- WakeOnLanInfos = new List<WakeOnLanInfo>();
-
- if (!string.IsNullOrEmpty(fullSystemInfo.MacAddress))
- {
- WakeOnLanInfos.Add(new WakeOnLanInfo
- {
- MacAddress = fullSystemInfo.MacAddress
- });
- }
- }
- }
-
- public string GetAddress(ConnectionMode mode)
- {
- switch (mode)
- {
- case ConnectionMode.Local:
- return LocalAddress;
- case ConnectionMode.Manual:
- return ManualAddress;
- case ConnectionMode.Remote:
- return RemoteAddress;
- default:
- throw new ArgumentException("Unexpected ConnectionMode");
- }
- }
-
- public void AddOrUpdate(ServerUserInfo user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- // Clone the existing list of users
- var list = new List<ServerUserInfo>();
- foreach (ServerUserInfo serverUserInfo in Users)
- {
- list.Add(serverUserInfo);
- }
-
- var index = FindIndex(list, user.Id);
-
- if (index != -1)
- {
- var existing = list[index];
-
- // Merge the data
- existing.IsSignedInOffline = user.IsSignedInOffline;
- }
- else
- {
- list.Add(user);
- }
-
- Users = list;
- }
-
- private int FindIndex(List<ServerUserInfo> users, string id)
- {
- var index = 0;
-
- foreach (var user in users)
- {
- if (StringHelper.EqualsIgnoreCase(id, user.Id))
- {
- return index;
- }
-
- index++;
- }
-
- return -1;
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ServerUserInfo.cs b/MediaBrowser.Model/ApiClient/ServerUserInfo.cs
deleted file mode 100644
index 812da7402..000000000
--- a/MediaBrowser.Model/ApiClient/ServerUserInfo.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ServerUserInfo
- {
- public string Id { get; set; }
- public bool IsSignedInOffline { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index e1b0514e7..d619e3140 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -13,9 +13,10 @@ namespace MediaBrowser.Model.Configuration
public string VaapiDevice { get; set; }
public int H264Crf { get; set; }
public string H264Preset { get; set; }
- public bool EnableHardwareDecoding { get; set; }
public bool EnableHardwareEncoding { get; set; }
+ public string[] HardwareDecodingCodecs { get; set; }
+
public EncodingOptions()
{
DownMixAudioBoost = 2;
@@ -24,8 +25,9 @@ namespace MediaBrowser.Model.Configuration
EncodingThreadCount = -1;
VaapiDevice = "/dev/dri/card0";
H264Crf = 23;
- EnableHardwareDecoding = true;
EnableHardwareEncoding = true;
+
+ HardwareDecodingCodecs = new string[] { "h264", "vc1" };
}
}
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index a570f7b10..db66837e4 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -162,7 +162,6 @@ namespace MediaBrowser.Model.Configuration
public bool EnableAutomaticRestart { get; set; }
public bool SkipDeserializationForBasicTypes { get; set; }
- public bool SkipDeserializationForAudio { get; set; }
public string ServerName { get; set; }
public string WanDdns { get; set; }
@@ -349,7 +348,9 @@ namespace MediaBrowser.Model.Configuration
Limit = 1,
Type = ImageType.Logo
}
- }
+ },
+
+ DisabledImageFetchers = new [] {"FanArt"}
},
new MetadataOptions(1, 1280)
@@ -539,7 +540,8 @@ namespace MediaBrowser.Model.Configuration
Type = ImageType.Thumb
}
},
- DisabledMetadataFetchers = new []{ "TheMovieDb" }
+ DisabledMetadataFetchers = new []{ "TheMovieDb" },
+ DisabledImageFetchers = new [] { "FanArt" }
},
new MetadataOptions(0, 1280)
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 30b5f384f..15bd003ae 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -25,7 +25,6 @@ namespace MediaBrowser.Model.Configuration
public string SubtitleLanguagePreference { get; set; }
public bool DisplayMissingEpisodes { get; set; }
- public bool DisplayUnairedEpisodes { get; set; }
public string[] GroupedFolders { get; set; }
diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs
index e04e04d21..14b1875c1 100644
--- a/MediaBrowser.Model/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Model/Dlna/CodecProfile.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
-using System.Linq;
namespace MediaBrowser.Model.Dlna
{
@@ -42,16 +41,9 @@ namespace MediaBrowser.Model.Dlna
return SplitValue(Codec);
}
- public List<string> GetContainers()
- {
- return SplitValue(Container);
- }
-
private bool ContainsContainer(string container)
{
- List<string> containers = GetContainers();
-
- return containers.Count == 0 || ListHelper.ContainsIgnoreCase(containers, container ?? string.Empty);
+ return ContainerProfile.ContainsContainer(Container, container);
}
public bool ContainsCodec(string codec, string container)
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 291096f75..bd3dc6fd2 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index 35d7ada6b..2004cfc1f 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
@@ -21,10 +22,15 @@ namespace MediaBrowser.Model.Dlna
public List<string> GetContainers()
{
+ return SplitValue(Container);
+ }
+
+ public static List<string> SplitValue(string value)
+ {
List<string> list = new List<string>();
- foreach (string i in (Container ?? string.Empty).Split(','))
+ foreach (string i in (value ?? string.Empty).Split(','))
{
- if (!string.IsNullOrEmpty(i)) list.Add(i);
+ if (!string.IsNullOrWhiteSpace(i)) list.Add(i);
}
return list;
}
@@ -33,7 +39,32 @@ namespace MediaBrowser.Model.Dlna
{
List<string> containers = GetContainers();
- return containers.Count == 0 || ListHelper.ContainsIgnoreCase(containers, container ?? string.Empty);
+ return ContainsContainer(containers, container);
+ }
+
+ public static bool ContainsContainer(string profileContainers, string inputContainer)
+ {
+ return ContainsContainer(SplitValue(profileContainers), inputContainer);
+ }
+
+ public static bool ContainsContainer(List<string> profileContainers, string inputContainer)
+ {
+ if (profileContainers.Count == 0)
+ {
+ return true;
+ }
+
+ var allInputContainers = SplitValue(inputContainer);
+
+ foreach (var container in allInputContainers)
+ {
+ if (ListHelper.ContainsIgnoreCase(profileContainers, container))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
}
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index cd7ff08d6..5f9bd772c 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -187,17 +187,14 @@ namespace MediaBrowser.Model.Dlna
public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
{
- container = StringHelper.TrimStart(container ?? string.Empty, '.');
-
foreach (var i in ResponseProfiles)
{
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
+ if (i.Type != DlnaProfileType.Audio)
{
continue;
}
- List<string> containers = i.GetContainers();
- if (containers.Count > 0 && !ListHelper.ContainsIgnoreCase(containers, container))
+ if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
@@ -208,7 +205,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
+ var conditionProcessor = new ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
@@ -230,9 +227,9 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private MediaBrowser.Model.Dlna.ProfileCondition GetModelProfileCondition(ProfileCondition c)
+ private ProfileCondition GetModelProfileCondition(ProfileCondition c)
{
- return new MediaBrowser.Model.Dlna.ProfileCondition
+ return new ProfileCondition
{
Condition = c.Condition,
IsRequired = c.IsRequired,
@@ -243,22 +240,19 @@ namespace MediaBrowser.Model.Dlna
public ResponseProfile GetImageMediaProfile(string container, int? width, int? height)
{
- container = StringHelper.TrimStart(container ?? string.Empty, '.');
-
foreach (var i in ResponseProfiles)
{
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Photo)
+ if (i.Type != DlnaProfileType.Photo)
{
continue;
}
- List<string> containers = i.GetContainers();
- if (containers.Count > 0 && !ListHelper.ContainsIgnoreCase(containers, container))
+ if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
- var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
+ var conditionProcessor = new ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
@@ -300,17 +294,14 @@ namespace MediaBrowser.Model.Dlna
string videoCodecTag,
bool? isAvc)
{
- container = StringHelper.TrimStart(container ?? string.Empty, '.');
-
foreach (var i in ResponseProfiles)
{
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
+ if (i.Type != DlnaProfileType.Video)
{
continue;
}
- List<string> containers = i.GetContainers();
- if (containers.Count > 0 && !ListHelper.ContainsIgnoreCase(containers, container ?? string.Empty))
+ if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
@@ -327,7 +318,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
+ var conditionProcessor = new ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index df511b0da..e80f59be4 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
@@ -19,27 +18,9 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
- public List<string> GetContainers()
- {
- List<string> list = new List<string>();
- foreach (string i in (Container ?? string.Empty).Split(','))
- {
- if (!string.IsNullOrEmpty(i)) list.Add(i);
- }
- return list;
- }
-
public bool SupportsContainer(string container)
{
- var all = GetContainers();
-
- // Only allow unknown container if the profile is all inclusive
- if (string.IsNullOrWhiteSpace(container))
- {
- return all.Count == 0;
- }
-
- return all.Count == 0 || all.Contains(container, StringComparer.OrdinalIgnoreCase);
+ return ContainerProfile.ContainsContainer(Container, container);
}
public List<string> GetAudioCodecs()
diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs
index 1d4791b5c..f1a001bba 100644
--- a/MediaBrowser.Model/Dlna/ResponseProfile.cs
+++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs
@@ -33,12 +33,7 @@ namespace MediaBrowser.Model.Dlna
public List<string> GetContainers()
{
- List<string> list = new List<string>();
- foreach (string i in (Container ?? string.Empty).Split(','))
- {
- if (!string.IsNullOrEmpty(i)) list.Add(i);
- }
- return list;
+ return ContainerProfile.SplitValue(Container);
}
public List<string> GetAudioCodecs()
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 48f9a4212..189ed27e4 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -197,6 +197,40 @@ namespace MediaBrowser.Model.Dlna
}
}
+ public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type)
+ {
+ if (string.IsNullOrWhiteSpace(inputContainer))
+ {
+ return null;
+ }
+
+ var formats = ContainerProfile.SplitValue(inputContainer);
+
+ if (formats.Count == 1)
+ {
+ return formats[0];
+ }
+
+ if (profile != null)
+ {
+ foreach (var format in formats)
+ {
+ foreach (var directPlayProfile in profile.DirectPlayProfiles)
+ {
+ if (directPlayProfile.Type == type)
+ {
+ if (directPlayProfile.SupportsContainer(format))
+ {
+ return format;
+ }
+ }
+ }
+ }
+ }
+
+ return formats[0];
+ }
+
private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options)
{
List<TranscodeReason> transcodeReasons = new List<TranscodeReason>();
@@ -214,14 +248,14 @@ namespace MediaBrowser.Model.Dlna
if (options.ForceDirectPlay)
{
playlistItem.PlayMethod = PlayMethod.DirectPlay;
- playlistItem.Container = item.Container;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
return playlistItem;
}
if (options.ForceDirectStream)
{
playlistItem.PlayMethod = PlayMethod.DirectStream;
- playlistItem.Container = item.Container;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
return playlistItem;
}
@@ -295,7 +329,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.PlayMethod = PlayMethod.DirectStream;
}
- playlistItem.Container = item.Container;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
return playlistItem;
}
@@ -648,7 +682,7 @@ namespace MediaBrowser.Model.Dlna
if (directPlay != null)
{
playlistItem.PlayMethod = directPlay.Value;
- playlistItem.Container = item.Container;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video);
if (subtitleStream != null)
{
@@ -1089,7 +1123,7 @@ namespace MediaBrowser.Model.Dlna
{
if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio))
{
- LogConditionFailure(profile, "VideoAudioCodecProfile", applyCondition, mediaSource);
+ LogConditionFailure(profile, "VideoAudioCodecProfile.ApplyConditions", applyCondition, mediaSource);
applyConditions = false;
break;
}
@@ -1231,21 +1265,27 @@ namespace MediaBrowser.Model.Dlna
private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer)
{
- if (string.Equals(transcodingContainer, "ts", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(transcodingContainer, "mpegts", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(transcodingContainer, "mp4", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(transcodingContainer, "mkv", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(transcodingContainer))
{
- return true;
+ var normalizedContainers = ContainerProfile.SplitValue(transcodingContainer);
+
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "ts"))
+ {
+ return false;
+ }
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "mpegts"))
+ {
+ return false;
+ }
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "mp4"))
+ {
+ return false;
+ }
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv") ||
+ ContainerProfile.ContainsContainer(normalizedContainers, "matroska"))
+ {
+ return true;
+ }
}
return false;
@@ -1572,14 +1612,17 @@ namespace MediaBrowser.Model.Dlna
}
// Check audio codec
- List<string> audioCodecs = profile.GetAudioCodecs();
- if (audioCodecs.Count > 0)
+ if (audioStream != null)
{
- // Check audio codecs
- string audioCodec = audioStream == null ? null : audioStream.Codec;
- if (string.IsNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
+ List<string> audioCodecs = profile.GetAudioCodecs();
+ if (audioCodecs.Count > 0)
{
- return false;
+ // Check audio codecs
+ string audioCodec = audioStream == null ? null : audioStream.Codec;
+ if (string.IsNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 9c8e8b030..f863d8c95 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -149,7 +149,7 @@ namespace MediaBrowser.Model.Dlna
list.Add(string.Format("{0}={1}", pair.Name, pair.Value));
}
- string queryString = string.Join("&", list.ToArray());
+ string queryString = string.Join("&", list.ToArray(list.Count));
return GetUrl(baseUrl, queryString);
}
@@ -203,7 +203,7 @@ namespace MediaBrowser.Model.Dlna
list.Add(pair.Value);
}
- return string.Format("Params={0}", string.Join(";", list.ToArray()));
+ return string.Format("Params={0}", string.Join(";", list.ToArray(list.Count)));
}
private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken, bool isDlna)
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 8077a5211..e0e7e55aa 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -163,7 +163,7 @@ namespace MediaBrowser.Model.Dto
public string[] ProductionLocations { get; set; }
- public List<string> MultiPartGameFiles { get; set; }
+ public string[] MultiPartGameFiles { get; set; }
/// <summary>
/// Gets or sets the path.
@@ -201,7 +201,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
- public List<string> Taglines { get; set; }
+ public string[] Taglines { get; set; }
/// <summary>
/// Gets or sets the genres.
@@ -210,12 +210,6 @@ namespace MediaBrowser.Model.Dto
public List<string> Genres { get; set; }
/// <summary>
- /// Gets or sets the series genres.
- /// </summary>
- /// <value>The series genres.</value>
- public List<string> SeriesGenres { get; set; }
-
- /// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
@@ -228,12 +222,6 @@ namespace MediaBrowser.Model.Dto
public long? CumulativeRunTimeTicks { get; set; }
/// <summary>
- /// Gets or sets the original run time ticks.
- /// </summary>
- /// <value>The original run time ticks.</value>
- public long? OriginalRunTimeTicks { get; set; }
-
- /// <summary>
/// Gets or sets the run time ticks.
/// </summary>
/// <value>The run time ticks.</value>
@@ -258,12 +246,6 @@ namespace MediaBrowser.Model.Dto
public int? ProductionYear { get; set; }
/// <summary>
- /// Gets or sets the players supported by a game.
- /// </summary>
- /// <value>The players.</value>
- public int? Players { get; set; }
-
- /// <summary>
/// Gets or sets a value indicating whether this instance is place holder.
/// </summary>
/// <value><c>null</c> if [is place holder] contains no value, <c>true</c> if [is place holder]; otherwise, <c>false</c>.</value>
@@ -298,7 +280,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the trailer urls.
/// </summary>
/// <value>The trailer urls.</value>
- public List<MediaUrl> RemoteTrailers { get; set; }
+ public MediaUrl[] RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the provider ids.
@@ -360,7 +342,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the parent backdrop image tags.
/// </summary>
/// <value>The parent backdrop image tags.</value>
- public List<string> ParentBackdropImageTags { get; set; }
+ public string[] ParentBackdropImageTags { get; set; }
/// <summary>
/// Gets or sets the local trailer count.
@@ -432,19 +414,13 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the air days.
/// </summary>
/// <value>The air days.</value>
- public List<DayOfWeek> AirDays { get; set; }
+ public DayOfWeek[] AirDays { get; set; }
/// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
- public List<string> Tags { get; set; }
-
- /// <summary>
- /// Gets or sets the keywords.
- /// </summary>
- /// <value>The keywords.</value>
- public List<string> Keywords { get; set; }
+ public string[] Tags { get; set; }
/// <summary>
/// Gets or sets the primary image aspect ratio, after image enhancements.
@@ -462,7 +438,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the artist items.
/// </summary>
/// <value>The artist items.</value>
- public List<NameIdPair> ArtistItems { get; set; }
+ public NameIdPair[] ArtistItems { get; set; }
/// <summary>
/// Gets or sets the album.
@@ -509,7 +485,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the album artists.
/// </summary>
/// <value>The album artists.</value>
- public List<NameIdPair> AlbumArtists { get; set; }
+ public NameIdPair[] AlbumArtists { get; set; }
/// <summary>
/// Gets or sets the name of the season.
@@ -530,12 +506,6 @@ namespace MediaBrowser.Model.Dto
public VideoType? VideoType { get; set; }
/// <summary>
- /// Gets or sets the display type of the media.
- /// </summary>
- /// <value>The display type of the media.</value>
- public string DisplayMediaType { get; set; }
-
- /// <summary>
/// Gets or sets the part count.
/// </summary>
/// <value>The part count.</value>
@@ -572,13 +542,13 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the backdrop image tags.
/// </summary>
/// <value>The backdrop image tags.</value>
- public List<string> BackdropImageTags { get; set; }
+ public string[] BackdropImageTags { get; set; }
/// <summary>
/// Gets or sets the screenshot image tags.
/// </summary>
/// <value>The screenshot image tags.</value>
- public List<string> ScreenshotImageTags { get; set; }
+ public string[] ScreenshotImageTags { get; set; }
/// <summary>
/// Gets or sets the parent logo image tag.
@@ -610,8 +580,6 @@ namespace MediaBrowser.Model.Dto
/// <value>The series studio.</value>
public string SeriesStudio { get; set; }
- public StudioDto SeriesStudioInfo { get; set; }
-
/// <summary>
/// Gets or sets the parent thumb item id.
/// </summary>
@@ -676,7 +644,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
- public List<MetadataFields> LockedFields { get; set; }
+ public MetadataFields[] LockedFields { get; set; }
/// <summary>
/// Gets or sets the trailer count.
@@ -754,66 +722,6 @@ namespace MediaBrowser.Model.Dto
public string SeriesTimerId { get; set; }
/// <summary>
- /// Gets a value indicating whether this instance has art.
- /// </summary>
- /// <value><c>true</c> if this instance has art; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasArtImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Art); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has logo.
- /// </summary>
- /// <value><c>true</c> if this instance has logo; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasLogo
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Logo); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has thumb.
- /// </summary>
- /// <value><c>true</c> if this instance has thumb; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasThumb
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Thumb); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has thumb.
- /// </summary>
- /// <value><c>true</c> if this instance has thumb; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasBackdrop
- {
- get { return (BackdropImageTags != null && BackdropImageTags.Count > 0) || (ParentBackdropImageTags != null && ParentBackdropImageTags.Count > 0); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has primary image.
- /// </summary>
- /// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasPrimaryImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Primary); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is video.
- /// </summary>
- /// <value><c>true</c> if this instance is video; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsVideo
- {
- get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Video); }
- }
-
- /// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 08824913f..1bf67f66c 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -53,7 +53,6 @@ namespace MediaBrowser.Model.Dto
public Video3DFormat? Video3DFormat { get; set; }
public List<MediaStream> MediaStreams { get; set; }
- public List<string> PlayableStreamFileNames { get; set; }
public List<string> Formats { get; set; }
@@ -73,7 +72,6 @@ namespace MediaBrowser.Model.Dto
Formats = new List<string>();
MediaStreams = new List<MediaStream>();
RequiredHttpHeaders = new Dictionary<string, string>();
- PlayableStreamFileNames = new List<string>();
SupportsTranscoding = true;
SupportsDirectStream = true;
SupportsDirectPlay = true;
@@ -164,7 +162,7 @@ namespace MediaBrowser.Model.Dto
{
foreach (MediaStream i in MediaStreams)
{
- if (i.Type == MediaStreamType.Video && StringHelper.IndexOfIgnoreCase(i.Codec ?? string.Empty, "jpeg") == -1)
+ if (i.Type == MediaStreamType.Video)
{
return i;
}
diff --git a/MediaBrowser.Model/Dto/StudioDto.cs b/MediaBrowser.Model/Dto/StudioDto.cs
deleted file mode 100644
index 10dc60699..000000000
--- a/MediaBrowser.Model/Dto/StudioDto.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Diagnostics;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class StudioDto
- /// </summary>
- [DebuggerDisplay("Name = {Name}")]
- public class StudioDto
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image tag.
- /// </summary>
- /// <value>The primary image tag.</value>
- public string PrimaryImageTag { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 3cd3e7dde..747528cbf 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -81,7 +81,7 @@ namespace MediaBrowser.Model.Entities
attributes.Add("Default");
}
- return string.Join(" ", attributes.ToArray());
+ return string.Join(" ", attributes.ToArray(attributes.Count));
}
if (Type == MediaStreamType.Subtitle)
@@ -107,7 +107,7 @@ namespace MediaBrowser.Model.Entities
attributes.Add("Forced");
}
- string name = string.Join(" ", attributes.ToArray());
+ string name = string.Join(" ", attributes.ToArray(attributes.Count));
return name;
}
diff --git a/MediaBrowser.Model/Entities/MetadataFields.cs b/MediaBrowser.Model/Entities/MetadataFields.cs
index a6c1388dc..a99fd0fe0 100644
--- a/MediaBrowser.Model/Entities/MetadataFields.cs
+++ b/MediaBrowser.Model/Entities/MetadataFields.cs
@@ -15,10 +15,6 @@ namespace MediaBrowser.Model.Entities
/// </summary>
Genres,
/// <summary>
- /// The keywords
- /// </summary>
- Keywords,
- /// <summary>
/// The production locations
/// </summary>
ProductionLocations,
diff --git a/MediaBrowser.Model/Entities/VideoType.cs b/MediaBrowser.Model/Entities/VideoType.cs
index aa9a3c55f..05c2fa32c 100644
--- a/MediaBrowser.Model/Entities/VideoType.cs
+++ b/MediaBrowser.Model/Entities/VideoType.cs
@@ -21,10 +21,6 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// The blu ray
/// </summary>
- BluRay,
- /// <summary>
- /// The hd DVD
- /// </summary>
- HdDvd
+ BluRay
}
}
diff --git a/MediaBrowser.Model/Extensions/LinqExtensions.cs b/MediaBrowser.Model/Extensions/LinqExtensions.cs
index 6b2bdb4c7..09ace42e8 100644
--- a/MediaBrowser.Model/Extensions/LinqExtensions.cs
+++ b/MediaBrowser.Model/Extensions/LinqExtensions.cs
@@ -42,6 +42,19 @@ namespace MediaBrowser.Model.Extensions
return source.DistinctBy(keySelector, null);
}
+ public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source, int count)
+ {
+ if (source == null) throw new ArgumentNullException("source");
+ if (count < 0) throw new ArgumentOutOfRangeException("count");
+ var array = new TSource[count];
+ int i = 0;
+ foreach (var item in source)
+ {
+ array[i++] = item;
+ }
+ return array;
+ }
+
/// <summary>
/// Returns all distinct elements of the given source, where "distinctness"
/// is determined via a projection and the specified comparer for the projected type.
diff --git a/MediaBrowser.Model/FileOrganization/AutoOrganizeOptions.cs b/MediaBrowser.Model/FileOrganization/AutoOrganizeOptions.cs
deleted file mode 100644
index 830d55bf5..000000000
--- a/MediaBrowser.Model/FileOrganization/AutoOrganizeOptions.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class AutoOrganizeOptions
- {
- /// <summary>
- /// Gets or sets the tv options.
- /// </summary>
- /// <value>The tv options.</value>
- public TvFileOrganizationOptions TvOptions { get; set; }
-
- /// <summary>
- /// Gets or sets a list of smart match entries.
- /// </summary>
- /// <value>The smart match entries.</value>
- public SmartMatchInfo[] SmartMatchInfos { get; set; }
-
- public AutoOrganizeOptions()
- {
- TvOptions = new TvFileOrganizationOptions();
- SmartMatchInfos = new SmartMatchInfo[]{};
- }
- }
-}
diff --git a/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs b/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs
deleted file mode 100644
index b20e43e54..000000000
--- a/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class EpisodeFileOrganizationRequest
- {
- public string ResultId { get; set; }
-
- public string SeriesId { get; set; }
-
- public int SeasonNumber { get; set; }
-
- public int EpisodeNumber { get; set; }
-
- public int? EndingEpisodeNumber { get; set; }
-
- public bool RememberCorrection { get; set; }
- public string NewSeriesName { get; set; }
-
- public string NewSeriesYear { get; set; }
-
- public string TargetFolder { get; set; }
-
- public Dictionary<string, string> NewSeriesProviderIds { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs b/MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs
deleted file mode 100644
index caf99183d..000000000
--- a/MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class FileOrganizationResult
- {
- /// <summary>
- /// Gets or sets the result identifier.
- /// </summary>
- /// <value>The result identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the original path.
- /// </summary>
- /// <value>The original path.</value>
- public string OriginalPath { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the original file.
- /// </summary>
- /// <value>The name of the original file.</value>
- public string OriginalFileName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the extracted.
- /// </summary>
- /// <value>The name of the extracted.</value>
- public string ExtractedName { get; set; }
-
- /// <summary>
- /// Gets or sets the extracted year.
- /// </summary>
- /// <value>The extracted year.</value>
- public int? ExtractedYear { get; set; }
-
- /// <summary>
- /// Gets or sets the extracted season number.
- /// </summary>
- /// <value>The extracted season number.</value>
- public int? ExtractedSeasonNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the extracted episode number.
- /// </summary>
- /// <value>The extracted episode number.</value>
- public int? ExtractedEpisodeNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the extracted ending episode number.
- /// </summary>
- /// <value>The extracted ending episode number.</value>
- public int? ExtractedEndingEpisodeNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the target path.
- /// </summary>
- /// <value>The target path.</value>
- public string TargetPath { get; set; }
-
- /// <summary>
- /// Gets or sets the date.
- /// </summary>
- /// <value>The date.</value>
- public DateTime Date { get; set; }
-
- /// <summary>
- /// Gets or sets the error message.
- /// </summary>
- /// <value>The error message.</value>
- public string StatusMessage { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public FileSortingStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public FileOrganizerType Type { get; set; }
-
- /// <summary>
- /// Gets or sets the duplicate paths.
- /// </summary>
- /// <value>The duplicate paths.</value>
- public List<string> DuplicatePaths { get; set; }
-
- /// <summary>
- /// Gets or sets the size of the file.
- /// </summary>
- /// <value>The size of the file.</value>
- public long FileSize { get; set; }
-
- /// <summary>
- /// Indicates if the item is currently being processed.
- /// </summary>
- /// <remarks>Runtime property not persisted to the store.</remarks>
- public bool IsInProgress { get; set; }
-
- public FileOrganizationResult()
- {
- DuplicatePaths = new List<string>();
- }
- }
-}
diff --git a/MediaBrowser.Model/FileOrganization/FileOrganizationResultQuery.cs b/MediaBrowser.Model/FileOrganization/FileOrganizationResultQuery.cs
deleted file mode 100644
index 18287534e..000000000
--- a/MediaBrowser.Model/FileOrganization/FileOrganizationResultQuery.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class FileOrganizationResultQuery
- {
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/FileOrganization/FileOrganizerType.cs b/MediaBrowser.Model/FileOrganization/FileOrganizerType.cs
deleted file mode 100644
index cbbeb9ce2..000000000
--- a/MediaBrowser.Model/FileOrganization/FileOrganizerType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.FileOrganization
-{
- public enum FileOrganizerType
- {
- Movie,
- Episode,
- Song
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/FileOrganization/FileSortingStatus.cs b/MediaBrowser.Model/FileOrganization/FileSortingStatus.cs
deleted file mode 100644
index 8a467c05f..000000000
--- a/MediaBrowser.Model/FileOrganization/FileSortingStatus.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.FileOrganization
-{
- public enum FileSortingStatus
- {
- Success,
- Failure,
- SkippedExisting
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs b/MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs
deleted file mode 100644
index 28c99b89b..000000000
--- a/MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class SmartMatchInfo
- {
- public string ItemName { get; set; }
- public string DisplayName { get; set; }
- public FileOrganizerType OrganizerType { get; set; }
- public string[] MatchStrings { get; set; }
-
- public SmartMatchInfo()
- {
- MatchStrings = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/FileOrganization/TvFileOrganizationOptions.cs b/MediaBrowser.Model/FileOrganization/TvFileOrganizationOptions.cs
deleted file mode 100644
index 973ecf6e7..000000000
--- a/MediaBrowser.Model/FileOrganization/TvFileOrganizationOptions.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-namespace MediaBrowser.Model.FileOrganization
-{
- public class TvFileOrganizationOptions
- {
- public bool IsEnabled { get; set; }
- public int MinFileSizeMb { get; set; }
- public string[] LeftOverFileExtensionsToDelete { get; set; }
- public string[] WatchLocations { get; set; }
-
- public string SeasonFolderPattern { get; set; }
-
- public string SeasonZeroFolderName { get; set; }
-
- public string EpisodeNamePattern { get; set; }
- public string MultiEpisodeNamePattern { get; set; }
-
- public bool OverwriteExistingEpisodes { get; set; }
-
- public bool DeleteEmptyFolders { get; set; }
-
- public bool CopyOriginalFile { get; set; }
-
- public TvFileOrganizationOptions()
- {
- MinFileSizeMb = 50;
-
- LeftOverFileExtensionsToDelete = new string[] { };
-
- WatchLocations = new string[] { };
-
- EpisodeNamePattern = "%sn - %sx%0e - %en.%ext";
- MultiEpisodeNamePattern = "%sn - %sx%0e-x%0ed - %en.%ext";
- SeasonFolderPattern = "Season %s";
- SeasonZeroFolderName = "Season 0";
-
- CopyOriginalFile = false;
- }
- }
-}
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 4477d8de3..47cec1459 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -12,12 +12,12 @@ namespace MediaBrowser.Model.Globalization
/// Gets the cultures.
/// </summary>
/// <returns>IEnumerable{CultureDto}.</returns>
- IEnumerable<CultureDto> GetCultures();
+ List<CultureDto> GetCultures();
/// <summary>
/// Gets the countries.
/// </summary>
/// <returns>IEnumerable{CountryInfo}.</returns>
- IEnumerable<CountryInfo> GetCountries();
+ List<CountryInfo> GetCountries();
/// <summary>
/// Gets the parental ratings.
/// </summary>
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index 8555f9c38..25185b4dd 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -46,13 +46,11 @@ namespace MediaBrowser.Model.LiveTv
public string FriendlyName { get; set; }
public bool ImportFavoritesOnly { get; set; }
public bool AllowHWTranscoding { get; set; }
- public bool EnableTvgId { get; set; }
public bool EnableStreamLooping { get; set; }
public TunerHostInfo()
{
AllowHWTranscoding = true;
- EnableTvgId = true;
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index e6cc58868..0e4cc0623 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -40,25 +40,10 @@
<Compile Include="Activity\ActivityLogEntry.cs" />
<Compile Include="Activity\IActivityManager.cs" />
<Compile Include="Activity\IActivityRepository.cs" />
- <Compile Include="ApiClient\ApiHelpers.cs" />
- <Compile Include="ApiClient\ConnectionMode.cs" />
- <Compile Include="ApiClient\ConnectionResult.cs" />
- <Compile Include="ApiClient\ConnectionState.cs" />
<Compile Include="ApiClient\ConnectSignupResponse.cs" />
<Compile Include="ApiClient\HttpResponseEventArgs.cs" />
- <Compile Include="ApiClient\IApiClient.cs" />
- <Compile Include="ApiClient\ApiClientExtensions.cs" />
- <Compile Include="ApiClient\IClientWebSocket.cs" />
- <Compile Include="ApiClient\IConnectionManager.cs" />
- <Compile Include="ApiClient\IDevice.cs" />
- <Compile Include="ApiClient\IServerEvents.cs" />
<Compile Include="ApiClient\GeneralCommandEventArgs.cs" />
- <Compile Include="ApiClient\NetworkStatus.cs" />
- <Compile Include="ApiClient\RemoteLogoutReason.cs" />
- <Compile Include="ApiClient\ServerCredentials.cs" />
<Compile Include="ApiClient\ServerDiscoveryInfo.cs" />
- <Compile Include="ApiClient\ServerInfo.cs" />
- <Compile Include="ApiClient\ServerUserInfo.cs" />
<Compile Include="ApiClient\SessionUpdatesEventArgs.cs" />
<Compile Include="ApiClient\WakeOnLanInfo.cs" />
<Compile Include="Branding\BrandingOptions.cs" />
@@ -85,7 +70,6 @@
<Compile Include="Connect\ConnectAuthenticationResult.cs" />
<Compile Include="Connect\ConnectAuthorization.cs" />
<Compile Include="Connect\ConnectAuthorizationRequest.cs" />
- <Compile Include="ApiClient\ConnectionOptions.cs" />
<Compile Include="Connect\ConnectPassword.cs" />
<Compile Include="Connect\ConnectUser.cs" />
<Compile Include="Connect\ConnectUserQuery.cs" />
@@ -143,7 +127,6 @@
<Compile Include="System\IPowerManagement.cs" />
<Compile Include="Text\ITextEncoding.cs" />
<Compile Include="Extensions\LinqExtensions.cs" />
- <Compile Include="FileOrganization\SmartMatchInfo.cs" />
<Compile Include="Health\IHealthMonitor.cs" />
<Compile Include="IO\FileSystemMetadata.cs" />
<Compile Include="IO\IFileSystem.cs" />
@@ -160,8 +143,6 @@
<Compile Include="Configuration\DynamicDayOfWeek.cs" />
<Compile Include="Entities\ExtraType.cs" />
<Compile Include="Entities\TrailerType.cs" />
- <Compile Include="FileOrganization\AutoOrganizeOptions.cs" />
- <Compile Include="FileOrganization\TvFileOrganizationOptions.cs" />
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
<Compile Include="Configuration\DlnaOptions.cs" />
<Compile Include="Configuration\ImageOption.cs" />
@@ -248,11 +229,6 @@
<Compile Include="Events\GenericEventArgs.cs" />
<Compile Include="Extensions\ListHelper.cs" />
<Compile Include="Extensions\StringHelper.cs" />
- <Compile Include="FileOrganization\EpisodeFileOrganizationRequest.cs" />
- <Compile Include="FileOrganization\FileOrganizationResult.cs" />
- <Compile Include="FileOrganization\FileOrganizationResultQuery.cs" />
- <Compile Include="FileOrganization\FileOrganizerType.cs" />
- <Compile Include="FileOrganization\FileSortingStatus.cs" />
<Compile Include="Globalization\LocalizatonOption.cs" />
<Compile Include="IO\FileSystemEntryType.cs" />
<Compile Include="Library\PlayAccess.cs" />
@@ -292,7 +268,6 @@
<Compile Include="Providers\ExternalUrl.cs" />
<Compile Include="Providers\ImageProviderInfo.cs" />
<Compile Include="Providers\RemoteImageInfo.cs" />
- <Compile Include="Dto\StudioDto.cs" />
<Compile Include="Entities\CollectionType.cs" />
<Compile Include="Entities\ItemReview.cs" />
<Compile Include="Entities\MediaUrl.cs" />
@@ -327,7 +302,6 @@
<Compile Include="Querying\NextUpQuery.cs" />
<Compile Include="Querying\QueryFilters.cs" />
<Compile Include="Querying\QueryResult.cs" />
- <Compile Include="Querying\SeasonQuery.cs" />
<Compile Include="Querying\SessionQuery.cs" />
<Compile Include="Querying\SimilarItemsQuery.cs" />
<Compile Include="Querying\UpcomingEpisodesQuery.cs" />
@@ -364,7 +338,6 @@
<Compile Include="IO\FileSystemEntryInfo.cs" />
<Compile Include="Dto\ImageOptions.cs" />
<Compile Include="Querying\ItemFilter.cs" />
- <Compile Include="Querying\ItemQuery.cs" />
<Compile Include="Entities\LibraryUpdateInfo.cs" />
<Compile Include="Entities\ParentalRating.cs" />
<Compile Include="Entities\VirtualFolderInfo.cs" />
diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
index 126710197..691dcc6c8 100644
--- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
@@ -7,6 +7,8 @@ namespace MediaBrowser.Model.MediaInfo
{
public class MediaInfo : MediaSourceInfo, IHasProviderIds
{
+ private static readonly string[] EmptyStringArray = new string[] { };
+
public List<ChapterInfo> Chapters { get; set; }
/// <summary>
@@ -23,7 +25,7 @@ namespace MediaBrowser.Model.MediaInfo
/// Gets or sets the album artists.
/// </summary>
/// <value>The album artists.</value>
- public List<string> AlbumArtists { get; set; }
+ public string[] AlbumArtists { get; set; }
/// <summary>
/// Gets or sets the studios.
/// </summary>
@@ -56,7 +58,7 @@ namespace MediaBrowser.Model.MediaInfo
{
Chapters = new List<ChapterInfo>();
Artists = new List<string>();
- AlbumArtists = new List<string>();
+ AlbumArtists = EmptyStringArray;
Studios = new List<string>();
Genres = new List<string>();
People = new List<BaseItemPerson>();
diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs
index 71eb9914b..42550340b 100644
--- a/MediaBrowser.Model/Net/ISocket.cs
+++ b/MediaBrowser.Model/Net/ISocket.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 2f132cb37..7a2e1f215 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -106,14 +106,15 @@ namespace MediaBrowser.Model.Net
return dict;
}
+ public static string GetMimeType(string path)
+ {
+ return GetMimeType(path, true);
+ }
+
/// <summary>
/// Gets the type of the MIME.
/// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">path</exception>
- /// <exception cref="InvalidOperationException">Argument not supported: + path</exception>
- public static string GetMimeType(string path)
+ public static string GetMimeType(string path, bool enableStreamDefault)
{
if (string.IsNullOrEmpty(path))
{
@@ -329,7 +330,12 @@ namespace MediaBrowser.Model.Net
return "application/ttml+xml";
}
- return "application/octet-stream";
+ if (enableStreamDefault)
+ {
+ return "application/octet-stream";
+ }
+
+ return null;
}
public static string ToExtension(string mimeType)
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index c6f34d58a..6cc6ba329 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -93,11 +93,6 @@
ItemCounts,
/// <summary>
- /// The keywords
- /// </summary>
- Keywords,
-
- /// <summary>
/// The media source count
/// </summary>
MediaSourceCount,
@@ -158,11 +153,6 @@
/// </summary>
ScreenshotImageTags,
- /// <summary>
- /// The series genres
- /// </summary>
- SeriesGenres,
-
SeriesPrimaryImage,
/// <summary>
@@ -229,6 +219,7 @@
SeriesPresentationUniqueKey,
DateLastRefreshed,
DateLastSaved,
- RefreshState
+ RefreshState,
+ ChannelImage
}
}
diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs
deleted file mode 100644
index 11c046452..000000000
--- a/MediaBrowser.Model/Querying/ItemQuery.cs
+++ /dev/null
@@ -1,332 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Contains all the possible parameters that can be used to query for items
- /// </summary>
- public class ItemQuery
- {
- /// <summary>
- /// The user to localize search results for
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Specify this to localize the search to a specific item or folder. Omit to use the root.
- /// </summary>
- /// <value>The parent id.</value>
- public string ParentId { get; set; }
-
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// What to sort the results by
- /// </summary>
- /// <value>The sort by.</value>
- public string[] SortBy { get; set; }
-
- /// <summary>
- /// Gets or sets the artist ids.
- /// </summary>
- /// <value>The artist ids.</value>
- public string[] ArtistIds { get; set; }
-
- /// <summary>
- /// The sort order to return results with
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder? SortOrder { get; set; }
-
- /// <summary>
- /// Filters to apply to the results
- /// </summary>
- /// <value>The filters.</value>
- public ItemFilter[] Filters { get; set; }
-
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
-
- /// <summary>
- /// Gets or sets the media types.
- /// </summary>
- /// <value>The media types.</value>
- public string[] MediaTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the video formats.
- /// </summary>
- /// <value>The video formats.</value>
- public bool? Is3D { get; set; }
-
- /// <summary>
- /// Gets or sets the video types.
- /// </summary>
- /// <value>The video types.</value>
- public VideoType[] VideoTypes { get; set; }
-
- /// <summary>
- /// Whether or not to perform the query recursively
- /// </summary>
- /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
- public bool Recursive { get; set; }
-
- /// <summary>
- /// Limit results to items containing specific genres
- /// </summary>
- /// <value>The genres.</value>
- public string[] Genres { get; set; }
-
- /// <summary>
- /// Gets or sets the studio ids.
- /// </summary>
- /// <value>The studio ids.</value>
- public string[] StudioIds { get; set; }
-
- /// <summary>
- /// Gets or sets the exclude item types.
- /// </summary>
- /// <value>The exclude item types.</value>
- public string[] ExcludeItemTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the include item types.
- /// </summary>
- /// <value>The include item types.</value>
- public string[] IncludeItemTypes { get; set; }
-
- /// <summary>
- /// Limit results to items containing specific years
- /// </summary>
- /// <value>The years.</value>
- public int[] Years { get; set; }
-
- /// <summary>
- /// Limit results to items containing a specific person
- /// </summary>
- /// <value>The person.</value>
- public string[] PersonIds { get; set; }
-
- /// <summary>
- /// If the Person filter is used, this can also be used to restrict to a specific person type
- /// </summary>
- /// <value>The type of the person.</value>
- public string[] PersonTypes { get; set; }
-
- /// <summary>
- /// Search characters used to find items
- /// </summary>
- /// <value>The index by.</value>
- public string SearchTerm { get; set; }
-
- /// <summary>
- /// Gets or sets the image types.
- /// </summary>
- /// <value>The image types.</value>
- public ImageType[] ImageTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the air days.
- /// </summary>
- /// <value>The air days.</value>
- public DayOfWeek[] AirDays { get; set; }
-
- /// <summary>
- /// Gets or sets the series status.
- /// </summary>
- /// <value>The series status.</value>
- public SeriesStatus[] SeriesStatuses { get; set; }
-
- /// <summary>
- /// Gets or sets the ids, which are specific items to retrieve
- /// </summary>
- /// <value>The ids.</value>
- public string[] Ids { get; set; }
-
- /// <summary>
- /// Gets or sets the min official rating.
- /// </summary>
- /// <value>The min official rating.</value>
- public string MinOfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the max official rating.
- /// </summary>
- /// <value>The max official rating.</value>
- public string MaxOfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the min index number.
- /// </summary>
- /// <value>The min index number.</value>
- public int? MinIndexNumber { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has parental rating.
- /// </summary>
- /// <value><c>null</c> if [has parental rating] contains no value, <c>true</c> if [has parental rating]; otherwise, <c>false</c>.</value>
- public bool? HasParentalRating { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is HD.
- /// </summary>
- /// <value><c>null</c> if [is HD] contains no value, <c>true</c> if [is HD]; otherwise, <c>false</c>.</value>
- public bool? IsHD { get; set; }
-
- /// <summary>
- /// Gets or sets the parent index number.
- /// </summary>
- /// <value>The parent index number.</value>
- public int? ParentIndexNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the min players.
- /// </summary>
- /// <value>The min players.</value>
- public int? MinPlayers { get; set; }
-
- /// <summary>
- /// Gets or sets the max players.
- /// </summary>
- /// <value>The max players.</value>
- public int? MaxPlayers { get; set; }
-
- /// <summary>
- /// Gets or sets the name starts with or greater.
- /// </summary>
- /// <value>The name starts with or greater.</value>
- public string NameStartsWithOrGreater { get; set; }
-
- /// <summary>
- /// Gets or sets the name starts with.
- /// </summary>
- /// <value>The name starts with or greater.</value>
- public string NameStartsWith { get; set; }
-
- /// <summary>
- /// Gets or sets the name starts with.
- /// </summary>
- /// <value>The name lessthan.</value>
- public string NameLessThan { get; set; }
-
- /// <summary>
- /// Gets or sets the album artist starts with or greater.
- /// </summary>
- /// <value>The album artist starts with or greater.</value>
- public string AlbumArtistStartsWithOrGreater { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [include index containers].
- /// </summary>
- /// <value><c>true</c> if [include index containers]; otherwise, <c>false</c>.</value>
- public bool IncludeIndexContainers { get; set; }
-
- /// <summary>
- /// Gets or sets the location types.
- /// </summary>
- /// <value>The location types.</value>
- public LocationType[] LocationTypes { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is missing episode.
- /// </summary>
- /// <value><c>null</c> if [is missing episode] contains no value, <c>true</c> if [is missing episode]; otherwise, <c>false</c>.</value>
- public bool? IsMissing { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is unaired episode.
- /// </summary>
- /// <value><c>null</c> if [is unaired episode] contains no value, <c>true</c> if [is unaired episode]; otherwise, <c>false</c>.</value>
- public bool? IsUnaired { get; set; }
-
- public bool? IsVirtualUnaired { get; set; }
-
- public bool? IsInBoxSet { get; set; }
-
- public bool? CollapseBoxSetItems { get; set; }
-
- public bool? IsPlayed { get; set; }
-
- /// <summary>
- /// Gets or sets the exclude location types.
- /// </summary>
- /// <value>The exclude location types.</value>
- public LocationType[] ExcludeLocationTypes { get; set; }
-
- public double? MinCommunityRating { get; set; }
- public double? MinCriticRating { get; set; }
-
- public int? AiredDuringSeason { get; set; }
-
- public DateTime? MinPremiereDate { get; set; }
-
- public DateTime? MaxPremiereDate { get; set; }
-
- public bool? EnableImages { get; set; }
- public int? ImageTypeLimit { get; set; }
- public ImageType[] EnableImageTypes { get; set; }
-
- [Obsolete]
- public string[] Artists { get; set; }
- [Obsolete]
- public string[] Studios { get; set; }
- [Obsolete]
- public string Person { get; set; }
-
- public bool EnableTotalRecordCount { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ItemQuery" /> class.
- /// </summary>
- public ItemQuery()
- {
- LocationTypes = new LocationType[] { };
- ExcludeLocationTypes = new LocationType[] { };
-
- SortBy = new string[] { };
-
- Filters = new ItemFilter[] { };
-
- Fields = new ItemFields[] { };
-
- MediaTypes = new string[] { };
-
- VideoTypes = new VideoType[] { };
-
- EnableTotalRecordCount = true;
-
- Artists = new string[] { };
- Studios = new string[] { };
-
- Genres = new string[] { };
- StudioIds = new string[] { };
- IncludeItemTypes = new string[] { };
- ExcludeItemTypes = new string[] { };
- Years = new int[] { };
- PersonTypes = new string[] { };
- Ids = new string[] { };
- ArtistIds = new string[] { };
- PersonIds = new string[] { };
-
- ImageTypes = new ImageType[] { };
- AirDays = new DayOfWeek[] { };
- SeriesStatuses = new SeriesStatus[] { };
- EnableImageTypes = new ImageType[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/SeasonQuery.cs b/MediaBrowser.Model/Querying/SeasonQuery.cs
deleted file mode 100644
index b1fe635bb..000000000
--- a/MediaBrowser.Model/Querying/SeasonQuery.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- public class SeasonQuery
- {
- public string UserId { get; set; }
-
- public string SeriesId { get; set; }
-
- public bool? IsMissing { get; set; }
-
- public bool? IsVirtualUnaired { get; set; }
-
- public ItemFields[] Fields { get; set; }
-
- public bool? IsSpecialSeason { get; set; }
-
- public SeasonQuery()
- {
- Fields = new ItemFields[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs
index 3ca0eafe6..5c5637481 100644
--- a/MediaBrowser.Model/Search/SearchHint.cs
+++ b/MediaBrowser.Model/Search/SearchHint.cs
@@ -91,12 +91,6 @@ namespace MediaBrowser.Model.Search
/// <value>The type of the media.</value>
public string MediaType { get; set; }
- /// <summary>
- /// Gets or sets the display type of the media.
- /// </summary>
- /// <value>The display type of the media.</value>
- public string DisplayMediaType { get; set; }
-
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs
index 46c0240cd..e1480f30a 100644
--- a/MediaBrowser.Model/Services/IHttpRequest.cs
+++ b/MediaBrowser.Model/Services/IHttpRequest.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Text;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Services/IHttpResponse.cs b/MediaBrowser.Model/Services/IHttpResponse.cs
index 377f303a7..cd9c07d46 100644
--- a/MediaBrowser.Model/Services/IHttpResponse.cs
+++ b/MediaBrowser.Model/Services/IHttpResponse.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs
index fcb137c6b..90afb0f27 100644
--- a/MediaBrowser.Model/Services/IHttpResult.cs
+++ b/MediaBrowser.Model/Services/IHttpResult.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs
index dfea62821..a3e00f587 100644
--- a/MediaBrowser.Model/Services/QueryParamCollection.cs
+++ b/MediaBrowser.Model/Services/QueryParamCollection.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Services
{
@@ -187,7 +188,7 @@ namespace MediaBrowser.Model.Services
public override String ToString()
{
- var vals = this.Select(GetQueryStringValue).ToArray();
+ var vals = this.Select(GetQueryStringValue).ToArray(this.Count);
return string.Join("&", vals);
}
diff --git a/MediaBrowser.Model/Social/ISharingRepository.cs b/MediaBrowser.Model/Social/ISharingRepository.cs
index 069b6e1fe..1dadd7f71 100644
--- a/MediaBrowser.Model/Social/ISharingRepository.cs
+++ b/MediaBrowser.Model/Social/ISharingRepository.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
namespace MediaBrowser.Model.Social
{
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
index f0e262c50..eb153427c 100644
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ b/MediaBrowser.Model/Sync/SyncJob.cs
@@ -105,12 +105,9 @@ namespace MediaBrowser.Model.Sync
public string PrimaryImageItemId { get; set; }
public string PrimaryImageTag { get; set; }
- public bool EnableAutomaticResync { get; set; }
-
public SyncJob()
{
RequestedItemIds = new List<string>();
- EnableAutomaticResync = true;
}
}
}
diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs
index 1c72ccd52..9fb275823 100644
--- a/MediaBrowser.Model/Sync/SyncJobItem.cs
+++ b/MediaBrowser.Model/Sync/SyncJobItem.cs
@@ -92,11 +92,6 @@ namespace MediaBrowser.Model.Sync
/// <value>The additional files.</value>
public List<ItemFileInfo> AdditionalFiles { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether this instance is marked for removal.
- /// </summary>
- /// <value><c>true</c> if this instance is marked for removal; otherwise, <c>false</c>.</value>
- public bool IsMarkedForRemoval { get; set; }
- /// <summary>
/// Gets or sets the index of the job item.
/// </summary>
/// <value>The index of the job item.</value>
diff --git a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs
index c4e23c63c..2a968869f 100644
--- a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs
+++ b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs
@@ -8,8 +8,6 @@ namespace MediaBrowser.Model.Sync
ReadyToTransfer = 2,
Transferring = 3,
Synced = 4,
- RemovedFromDevice = 5,
- Failed = 6,
- Cancelled = 7
+ Failed = 5
}
}
diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs
index ac211a32a..2d1d30802 100644
--- a/MediaBrowser.Model/Sync/SyncJobStatus.cs
+++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs
@@ -9,7 +9,6 @@ namespace MediaBrowser.Model.Sync
Transferring = 3,
Completed = 4,
CompletedWithError = 5,
- Failed = 6,
- Cancelled = 7
+ Failed = 6
}
}
diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
index 66f5294e7..2dec79e93 100644
--- a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
+++ b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Linq;
-
+
namespace MediaBrowser.Model.Tasks
{
/// <summary>
@@ -26,12 +24,6 @@ namespace MediaBrowser.Model.Tasks
string key = task.ScheduledTask.Key;
- var triggers = task.Triggers
- .OrderBy(i => i.Type)
- .ThenBy(i => i.DayOfWeek ?? DayOfWeek.Sunday)
- .ThenBy(i => i.TimeOfDayTicks ?? 0)
- .ToList();
-
return new TaskInfo
{
Name = task.Name,
@@ -40,7 +32,7 @@ namespace MediaBrowser.Model.Tasks
Id = task.Id,
LastExecutionResult = task.LastExecutionResult,
- Triggers = triggers,
+ Triggers = task.Triggers,
Description = task.Description,
Category = task.Category,
diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs
index 50276f8eb..8792ce952 100644
--- a/MediaBrowser.Model/Tasks/TaskInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskInfo.cs
@@ -41,7 +41,7 @@ namespace MediaBrowser.Model.Tasks
/// Gets or sets the triggers.
/// </summary>
/// <value>The triggers.</value>
- public List<TaskTriggerInfo> Triggers { get; set; }
+ public TaskTriggerInfo[] Triggers { get; set; }
/// <summary>
/// Gets or sets the description.
@@ -72,7 +72,7 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
public TaskInfo()
{
- Triggers = new List<TaskTriggerInfo>();
+ Triggers = new TaskTriggerInfo[]{};
}
}
}
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index e2a75c56b..84ee5d637 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -68,6 +68,8 @@ namespace MediaBrowser.Model.Users
public string[] BlockedMediaFolders { get; set; }
public string[] BlockedChannels { get; set; }
+ public int RemoteClientBitrateLimit { get; set; }
+
public UserPolicy()
{
EnableContentDeletion = true;
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
index 7cce85105..b9e265d22 100644
--- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.Books
{
public class AudioBookMetadataService : MetadataService<AudioBook, SongInfo>
{
- protected override void MergeData(MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
index 219f95799..2ea0a7ee9 100644
--- a/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
+++ b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.Books
{
public class AudioPodcastMetadataService : MetadataService<AudioPodcast, SongInfo>
{
- protected override void MergeData(MetadataResult<AudioPodcast> source, MetadataResult<AudioPodcast> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<AudioPodcast> source, MetadataResult<AudioPodcast> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs
index ae7e734cc..68b96486a 100644
--- a/MediaBrowser.Providers/Books/BookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/BookMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Books
{
public class BookMetadataService : MetadataService<Book, BookInfo>
{
- protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index b391ce4d3..81cd41605 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -6,12 +6,9 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.BoxSets
{
@@ -35,7 +32,7 @@ namespace MediaBrowser.Providers.BoxSets
return updateType;
}
- protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@@ -48,7 +45,7 @@ namespace MediaBrowser.Providers.BoxSets
var linkedChildren = sourceItem.LinkedChildren.ToList();
linkedChildren.AddRange(sourceItem.LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut));
- targetItem.LinkedChildren = linkedChildren;
+ targetItem.LinkedChildren = linkedChildren.ToArray(linkedChildren.Count);
targetItem.Shares = sourceItem.Shares;
}
}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index 852feab37..7d1f2779b 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
@@ -33,12 +33,12 @@ namespace MediaBrowser.Providers.BoxSets
get { return "TheMovieDb"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is BoxSet;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.BoxSets
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
@@ -123,8 +123,7 @@ namespace MediaBrowser.Providers.BoxSets
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
/// <summary>
@@ -145,8 +144,7 @@ namespace MediaBrowser.Providers.BoxSets
private IEnumerable<MovieDbBoxSetProvider.Backdrop> GetBackdrops(MovieDbBoxSetProvider.Images images)
{
var eligibleBackdrops = images.backdrops == null ? new List<MovieDbBoxSetProvider.Backdrop>() :
- images.backdrops
- .ToList();
+ images.backdrops;
return eligibleBackdrops.OrderByDescending(i => i.vote_average)
.ThenByDescending(i => i.vote_count);
diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
index 8a3da5bf4..b22db559e 100644
--- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Channels
{
public class ChannelMetadataService : MetadataService<Channel, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
index 5fea2c3cd..3973d3de7 100644
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs
@@ -39,9 +39,9 @@ namespace MediaBrowser.Providers.Chapters
return _itemRepo.GetChapters(new Guid(itemId));
}
- public Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken)
+ public Task SaveChapters(string itemId, List<ChapterInfo> chapters)
{
- return _itemRepo.SaveChapters(new Guid(itemId), chapters, cancellationToken);
+ return _itemRepo.SaveChapters(new Guid(itemId), chapters);
}
}
}
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index dc0fda72e..1dab08671 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Folders
{
public class CollectionFolderMetadataService : MetadataService<CollectionFolder, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
@@ -27,7 +27,7 @@ namespace MediaBrowser.Providers.Folders
public class ManualCollectionsFolderMetadataService : MetadataService<ManualCollectionsFolder, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<ManualCollectionsFolder> source, MetadataResult<ManualCollectionsFolder> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<ManualCollectionsFolder> source, MetadataResult<ManualCollectionsFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
index ff8d87e2b..687dac919 100644
--- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Folders
}
}
- protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
index 06b62a98b..951794961 100644
--- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Folders
{
public class UserViewMetadataService : MetadataService<UserView, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
index 13d40b4d9..edde0f5e3 100644
--- a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
+++ b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.GameGenres
{
public class GameGenreMetadataService : MetadataService<GameGenre, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<GameGenre> source, MetadataResult<GameGenre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<GameGenre> source, MetadataResult<GameGenre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Games/GameMetadataService.cs b/MediaBrowser.Providers/Games/GameMetadataService.cs
index 10f74629f..67becbf58 100644
--- a/MediaBrowser.Providers/Games/GameMetadataService.cs
+++ b/MediaBrowser.Providers/Games/GameMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Games
{
public class GameMetadataService : MetadataService<Game, GameInfo>
{
- protected override void MergeData(MetadataResult<Game> source, MetadataResult<Game> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Game> source, MetadataResult<Game> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
index ca33563fa..474dd2fcf 100644
--- a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
+++ b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Games
{
public class GameSystemMetadataService : MetadataService<GameSystem, GameSystemInfo>
{
- protected override void MergeData(MetadataResult<GameSystem> source, MetadataResult<GameSystem> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<GameSystem> source, MetadataResult<GameSystem> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
index a695fb372..88fba1854 100644
--- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs
+++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Genres
{
public class GenreMetadataService : MetadataService<Genre, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
index 499fd2e0b..74c01fb5c 100644
--- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
+++ b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
@@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.ImagesByName
return file;
}
- public static string FindMatch(IHasImages item, IEnumerable<string> images)
+ public static string FindMatch(IHasMetadata item, IEnumerable<string> images)
{
var name = GetComparableName(item.Name);
diff --git a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
index f4749c37d..509c91188 100644
--- a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
+++ b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.LiveTv
{
public class AudioRecordingService : MetadataService<LiveTvAudioRecording, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<LiveTvAudioRecording> source, MetadataResult<LiveTvAudioRecording> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<LiveTvAudioRecording> source, MetadataResult<LiveTvAudioRecording> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
index 8012021ab..31e3ecaf4 100644
--- a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.LiveTv
{
public class ChannelMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
index f203aa8c6..28a12540b 100644
--- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.LiveTv
{
public class ProgramMetadataService : MetadataService<LiveTvProgram, LiveTvProgramLookupInfo>
{
- protected override void MergeData(MetadataResult<LiveTvProgram> source, MetadataResult<LiveTvProgram> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<LiveTvProgram> source, MetadataResult<LiveTvProgram> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
index 528e9a5ec..8bfa91655 100644
--- a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
+++ b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.LiveTv
{
public class VideoRecordingService : MetadataService<LiveTvVideoRecording, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<LiveTvVideoRecording> source, MetadataResult<LiveTvVideoRecording> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<LiveTvVideoRecording> source, MetadataResult<LiveTvVideoRecording> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index dc6c7e43f..7fdbdbcc7 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -17,6 +17,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Manager
{
@@ -67,12 +68,12 @@ namespace MediaBrowser.Providers.Manager
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">mimeType</exception>
- public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
}
- public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
+ public async Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(mimeType))
{
@@ -274,7 +275,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="mimeType">Type of the MIME.</param>
/// <param name="saveLocally">if set to <c>true</c> [save locally].</param>
/// <returns>IEnumerable{System.String}.</returns>
- private string[] GetSavePaths(IHasImages item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
+ private string[] GetSavePaths(IHasMetadata item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
if (!saveLocally || (_config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy))
{
@@ -296,7 +297,7 @@ namespace MediaBrowser.Providers.Manager
/// or
/// imageIndex
/// </exception>
- private ItemImageInfo GetCurrentImage(IHasImages item, ImageType type, int imageIndex)
+ private ItemImageInfo GetCurrentImage(IHasMetadata item, ImageType type, int imageIndex)
{
return item.GetImageInfo(type, imageIndex);
}
@@ -311,7 +312,7 @@ namespace MediaBrowser.Providers.Manager
/// <exception cref="System.ArgumentNullException">imageIndex
/// or
/// imageIndex</exception>
- private void SetImagePath(IHasImages item, ImageType type, int? imageIndex, string path)
+ private void SetImagePath(IHasMetadata item, ImageType type, int? imageIndex, string path)
{
item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
}
@@ -330,7 +331,7 @@ namespace MediaBrowser.Providers.Manager
/// or
/// imageIndex
/// </exception>
- private string GetStandardSavePath(IHasImages item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
+ private string GetStandardSavePath(IHasMetadata item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
var season = item as Season;
var extension = MimeTypes.ToExtension(mimeType);
@@ -355,7 +356,7 @@ namespace MediaBrowser.Providers.Manager
return Path.Combine(seriesFolder, imageFilename);
}
- if (item.DetectIsInMixedFolder())
+ if (item.IsInMixedFolder)
{
return GetSavePathForItemInMixedFolder(item, type, "landscape", extension);
}
@@ -432,7 +433,7 @@ namespace MediaBrowser.Providers.Manager
path = Path.Combine(_fileSystem.GetDirectoryName(item.Path), "metadata", filename + extension);
}
- else if (item.DetectIsInMixedFolder())
+ else if (item.IsInMixedFolder)
{
path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
}
@@ -483,7 +484,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="mimeType">Type of the MIME.</param>
/// <returns>IEnumerable{System.String}.</returns>
/// <exception cref="System.ArgumentNullException">imageIndex</exception>
- private string[] GetCompatibleSavePaths(IHasImages item, ImageType type, int? imageIndex, string mimeType)
+ private string[] GetCompatibleSavePaths(IHasMetadata item, ImageType type, int? imageIndex, string mimeType)
{
var season = item as Season;
@@ -499,7 +500,7 @@ namespace MediaBrowser.Providers.Manager
if (imageIndex.Value == 0)
{
- if (item.DetectIsInMixedFolder())
+ if (item.IsInMixedFolder)
{
return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart", extension) };
}
@@ -525,7 +526,7 @@ namespace MediaBrowser.Providers.Manager
var outputIndex = imageIndex.Value;
- if (item.DetectIsInMixedFolder())
+ if (item.IsInMixedFolder)
{
return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart" + outputIndex.ToString(UsCulture), extension) };
}
@@ -541,7 +542,7 @@ namespace MediaBrowser.Providers.Manager
{
list.Add(Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension));
}
- return list.ToArray();
+ return list.ToArray(list.Count);
}
if (type == ImageType.Primary)
@@ -568,7 +569,7 @@ namespace MediaBrowser.Providers.Manager
return new[] { Path.Combine(seasonFolder, imageFilename) };
}
- if (item.DetectIsInMixedFolder() || item is MusicVideo)
+ if (item.IsInMixedFolder || item is MusicVideo)
{
return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) };
}
@@ -603,7 +604,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="imageFilename">The image filename.</param>
/// <param name="extension">The extension.</param>
/// <returns>System.String.</returns>
- private string GetSavePathForItemInMixedFolder(IHasImages item, ImageType type, string imageFilename, string extension)
+ private string GetSavePathForItemInMixedFolder(IHasMetadata item, ImageType type, string imageFilename, string extension)
{
if (type == ImageType.Primary)
{
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 39715a449..e62ce0225 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -23,6 +23,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Manager
{
@@ -41,7 +42,7 @@ namespace MediaBrowser.Providers.Manager
_fileSystem = fileSystem;
}
- public bool ValidateImages(IHasImages item, IEnumerable<IImageProvider> providers, IDirectoryService directoryService)
+ public bool ValidateImages(IHasMetadata item, IEnumerable<IImageProvider> providers, IDirectoryService directoryService)
{
var hasChanges = false;
@@ -60,7 +61,7 @@ namespace MediaBrowser.Providers.Manager
return hasChanges;
}
- public async Task<RefreshResult> RefreshImages(IHasImages item, LibraryOptions libraryOptions, IEnumerable<IImageProvider> imageProviders, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
+ public async Task<RefreshResult> RefreshImages(IHasMetadata item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
{
if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
{
@@ -73,8 +74,6 @@ namespace MediaBrowser.Providers.Manager
var result = new RefreshResult { UpdateType = ItemUpdateType.None };
- var providers = imageProviders.ToList();
-
var providerIds = new List<Guid>();
// In order to avoid duplicates, only download these if there are none already
@@ -118,7 +117,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task RefreshFromProvider(IHasImages item,
+ private async Task RefreshFromProvider(IHasMetadata item,
IDynamicImageProvider provider,
ImageRefreshOptions refreshOptions,
MetadataOptions savedOptions,
@@ -203,7 +202,7 @@ namespace MediaBrowser.Providers.Manager
ImageType.Thumb
};
- private bool HasImage(IHasImages item, ImageType type)
+ private bool HasImage(IHasMetadata item, ImageType type)
{
var image = item.GetImageInfo(type, 0);
@@ -220,7 +219,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="backdropLimit">The backdrop limit.</param>
/// <param name="screenshotLimit">The screenshot limit.</param>
/// <returns><c>true</c> if the specified item contains images; otherwise, <c>false</c>.</returns>
- private bool ContainsImages(IHasImages item, List<ImageType> images, MetadataOptions savedOptions, int backdropLimit, int screenshotLimit)
+ private bool ContainsImages(IHasMetadata item, List<ImageType> images, MetadataOptions savedOptions, int backdropLimit, int screenshotLimit)
{
if (_singularImages.Any(i => images.Contains(i) && !HasImage(item, i) && savedOptions.GetLimit(i) > 0))
{
@@ -253,7 +252,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task RefreshFromProvider(IHasImages item, LibraryOptions libraryOptions,
+ private async Task RefreshFromProvider(IHasMetadata item, LibraryOptions libraryOptions,
IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions,
MetadataOptions savedOptions,
@@ -333,7 +332,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool IsEnabled(MetadataOptions options, ImageType type, IHasImages item)
+ private bool IsEnabled(MetadataOptions options, ImageType type, IHasMetadata item)
{
if (type == ImageType.Backdrop)
{
@@ -360,7 +359,7 @@ namespace MediaBrowser.Providers.Manager
return options.IsEnabled(type);
}
- private void ClearImages(IHasImages item, ImageType type)
+ private void ClearImages(IHasMetadata item, ImageType type)
{
var deleted = false;
var deletedImages = new List<ItemImageInfo>();
@@ -384,10 +383,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- foreach (var image in deletedImages)
- {
- item.RemoveImage(image);
- }
+ item.RemoveImages(deletedImages);
if (deleted)
{
@@ -395,7 +391,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- public bool MergeImages(IHasImages item, List<LocalImageInfo> images)
+ public bool MergeImages(IHasMetadata item, List<LocalImageInfo> images)
{
var changed = false;
@@ -453,7 +449,7 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
- private bool UpdateMultiImages(IHasImages item, List<LocalImageInfo> images, ImageType type)
+ private bool UpdateMultiImages(IHasMetadata item, List<LocalImageInfo> images, ImageType type)
{
var changed = false;
@@ -471,7 +467,7 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
- private async Task<bool> DownloadImage(IHasImages item, LibraryOptions libraryOptions,
+ private async Task<bool> DownloadImage(IHasMetadata item, LibraryOptions libraryOptions,
IRemoteImageProvider provider,
RefreshResult result,
IEnumerable<RemoteImageInfo> images,
@@ -517,7 +513,7 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- private bool EnableImageStub(IHasImages item, ImageType type, LibraryOptions libraryOptions)
+ private bool EnableImageStub(IHasMetadata item, ImageType type, LibraryOptions libraryOptions)
{
if (item is LiveTvProgram)
{
@@ -557,14 +553,14 @@ namespace MediaBrowser.Providers.Manager
}
}
- private void SaveImageStub(IHasImages item, ImageType imageType, IEnumerable<string> urls)
+ private void SaveImageStub(IHasMetadata item, ImageType imageType, IEnumerable<string> urls)
{
var newIndex = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
SaveImageStub(item, imageType, urls, newIndex);
}
- private void SaveImageStub(IHasImages item, ImageType imageType, IEnumerable<string> urls, int newIndex)
+ private void SaveImageStub(IHasMetadata item, ImageType imageType, IEnumerable<string> urls, int newIndex)
{
var path = string.Join("|", urls.Take(1).ToArray());
@@ -576,7 +572,7 @@ namespace MediaBrowser.Providers.Manager
}, newIndex);
}
- private async Task DownloadBackdrops(IHasImages item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
+ private async Task DownloadBackdrops(IHasMetadata item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == imageType))
{
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index edca5e7d6..5d531c95c 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.Manager
{
var baseItem = result.Item as BaseItem;
- await LibraryManager.UpdatePeople(baseItem, result.People.ToList());
+ await LibraryManager.UpdatePeople(baseItem, result.People);
await SavePeopleMetadata(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
}
await result.Item.UpdateToRepository(reason, cancellationToken).ConfigureAwait(false);
@@ -302,13 +302,6 @@ namespace MediaBrowser.Providers.Manager
updateType |= ItemUpdateType.MetadataImport;
}
- var inheritedTags = item.GetInheritedTags();
- if (!inheritedTags.SequenceEqual(item.InheritedTags, StringComparer.Ordinal))
- {
- item.InheritedTags = inheritedTags;
- updateType |= ItemUpdateType.MetadataImport;
- }
-
return updateType;
}
@@ -321,7 +314,7 @@ namespace MediaBrowser.Providers.Manager
var folder = item as Folder;
if (folder != null && folder.SupportsCumulativeRunTimeTicks)
{
- var items = folder.GetRecursiveChildren(i => !i.IsFolder).ToList();
+ var items = folder.GetRecursiveChildren(i => !i.IsFolder);
var ticks = items.Select(i => i.RunTimeTicks ?? 0).Sum();
if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks)
@@ -526,7 +519,7 @@ namespace MediaBrowser.Providers.Manager
userDataList.AddRange(localItem.UserDataList);
}
- MergeData(localItem, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
+ MergeData(localItem, temp, new MetadataFields[]{}, !options.ReplaceAllMetadata, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
@@ -574,7 +567,7 @@ namespace MediaBrowser.Providers.Manager
else
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
- MergeData(metadata, temp, new List<MetadataFields>(), false, false);
+ MergeData(metadata, temp, new MetadataFields[]{}, false, false);
MergeData(temp, metadata, item.LockedFields, true, false);
}
}
@@ -711,7 +704,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var result in results)
{
- MergeData(result, temp, new List<MetadataFields>(), false, false);
+ MergeData(result, temp, new MetadataFields[]{}, false, false);
}
return refreshResult;
@@ -743,7 +736,7 @@ namespace MediaBrowser.Providers.Manager
protected abstract void MergeData(MetadataResult<TItemType> source,
MetadataResult<TItemType> target,
- List<MetadataFields> lockedFields,
+ MetadataFields[] lockedFields,
bool replaceData,
bool mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index ce995fe64..139bd6d58 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -128,7 +128,7 @@ namespace MediaBrowser.Providers.Manager
return Task.FromResult(ItemUpdateType.None);
}
- public async Task SaveImage(IHasImages item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public async Task SaveImage(IHasMetadata item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
var response = await _httpClient.GetResponse(new HttpRequestOptions
{
@@ -142,12 +142,12 @@ namespace MediaBrowser.Providers.Manager
.ConfigureAwait(false);
}
- public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
}
- public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
+ public Task SaveImage(IHasMetadata item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(source))
{
@@ -159,7 +159,7 @@ namespace MediaBrowser.Providers.Manager
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
- public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasMetadata item, RemoteImageQuery query, CancellationToken cancellationToken)
{
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);
@@ -182,9 +182,7 @@ namespace MediaBrowser.Providers.Manager
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var images = results.SelectMany(i => i.ToList());
-
- return images;
+ return results.SelectMany(i => i.ToList());
}
/// <summary>
@@ -196,7 +194,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="preferredLanguages">The preferred languages.</param>
/// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null)
+ private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null)
{
try
{
@@ -232,7 +230,7 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
- public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasImages item)
+ public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasMetadata item)
{
return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo
{
@@ -241,12 +239,12 @@ namespace MediaBrowser.Providers.Manager
});
}
- public IEnumerable<IImageProvider> GetImageProviders(IHasImages item, ImageRefreshOptions refreshOptions)
+ public IEnumerable<IImageProvider> GetImageProviders(IHasMetadata item, ImageRefreshOptions refreshOptions)
{
return GetImageProviders(item, GetMetadataOptions(item), refreshOptions, false);
}
- private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
+ private IEnumerable<IImageProvider> GetImageProviders(IHasMetadata item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
{
// Avoid implicitly captured closure
var currentOptions = options;
@@ -291,7 +289,7 @@ namespace MediaBrowser.Providers.Manager
.ThenBy(GetDefaultOrder);
}
- private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item, bool includeDisabled)
+ private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasMetadata item, bool includeDisabled)
{
var options = GetMetadataOptions(item);
@@ -339,7 +337,7 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- private bool CanRefresh(IImageProvider provider, IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
+ private bool CanRefresh(IImageProvider provider, IHasMetadata item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
{
if (!includeDisabled)
{
@@ -530,7 +528,7 @@ namespace MediaBrowser.Providers.Manager
}
private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders)
- where T : IHasImages
+ where T : IHasMetadata
{
// Locals
@@ -550,7 +548,7 @@ namespace MediaBrowser.Providers.Manager
}));
}
- public MetadataOptions GetMetadataOptions(IHasImages item)
+ public MetadataOptions GetMetadataOptions(IHasMetadata item)
{
var type = item.GetType().Name;
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
index 042b7241b..af91f5e02 100644
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Manager
{
public static void MergeBaseItemData<T>(MetadataResult<T> sourceResult,
MetadataResult<T> targetResult,
- List<MetadataFields> lockedFields,
+ MetadataFields[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
where T : BaseItem
@@ -150,7 +150,7 @@ namespace MediaBrowser.Providers.Manager
if (!lockedFields.Contains(MetadataFields.Studios))
{
- if (replaceData || target.Studios.Count == 0)
+ if (replaceData || target.Studios.Length == 0)
{
target.Studios = source.Studios;
}
@@ -158,23 +158,15 @@ namespace MediaBrowser.Providers.Manager
if (!lockedFields.Contains(MetadataFields.Tags))
{
- if (replaceData || target.Tags.Count == 0)
+ if (replaceData || target.Tags.Length == 0)
{
target.Tags = source.Tags;
}
}
- if (!lockedFields.Contains(MetadataFields.Keywords))
- {
- if (replaceData || target.Keywords.Count == 0)
- {
- target.Keywords = source.Keywords;
- }
- }
-
if (!lockedFields.Contains(MetadataFields.ProductionLocations))
{
- if (replaceData || target.ProductionLocations.Count == 0)
+ if (replaceData || target.ProductionLocations.Length == 0)
{
target.ProductionLocations = source.ProductionLocations;
}
@@ -212,12 +204,17 @@ namespace MediaBrowser.Providers.Manager
//if (!lockedFields.Contains(MetadataFields.DisplayMediaType))
{
- if (replaceData || string.IsNullOrEmpty(target.DisplayMediaType))
+ var targetVideo = target as Video;
+ var sourceVideo = source as Video;
+ if (sourceVideo != null && targetVideo != null)
{
- // Safeguard against incoming data having an emtpy name
- if (!string.IsNullOrWhiteSpace(source.DisplayMediaType))
+ if (replaceData || string.IsNullOrEmpty(targetVideo.DisplayMediaType))
{
- target.DisplayMediaType = source.DisplayMediaType;
+ // Safeguard against incoming data having an emtpy name
+ if (!string.IsNullOrWhiteSpace(sourceVideo.DisplayMediaType))
+ {
+ targetVideo.DisplayMediaType = sourceVideo.DisplayMediaType;
+ }
}
}
}
@@ -269,7 +266,7 @@ namespace MediaBrowser.Providers.Manager
target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
}
- private static void MergeDisplayOrder(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ private static void MergeDisplayOrder(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
var sourceHasDisplayOrder = source as IHasDisplayOrder;
var targetHasDisplayOrder = target as IHasDisplayOrder;
@@ -280,21 +277,21 @@ namespace MediaBrowser.Providers.Manager
}
}
- private static void MergeAlbumArtist(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ private static void MergeAlbumArtist(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
var sourceHasAlbumArtist = source as IHasAlbumArtist;
var targetHasAlbumArtist = target as IHasAlbumArtist;
if (sourceHasAlbumArtist != null && targetHasAlbumArtist != null)
{
- if (replaceData || targetHasAlbumArtist.AlbumArtists.Count == 0)
+ if (replaceData || targetHasAlbumArtist.AlbumArtists.Length == 0)
{
targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists;
}
}
}
- private static void MergeCriticRating(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ private static void MergeCriticRating(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
if (replaceData || !target.CriticRating.HasValue)
{
@@ -302,21 +299,21 @@ namespace MediaBrowser.Providers.Manager
}
}
- private static void MergeTrailers(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ private static void MergeTrailers(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
var sourceCast = source as IHasTrailers;
var targetCast = target as IHasTrailers;
if (sourceCast != null && targetCast != null)
{
- if (replaceData || targetCast.RemoteTrailers.Count == 0)
+ if (replaceData || targetCast.RemoteTrailers.Length == 0)
{
targetCast.RemoteTrailers = sourceCast.RemoteTrailers;
}
}
}
- private static void MergeVideoInfo(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ private static void MergeVideoInfo(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
var sourceCast = source as Video;
var targetCast = target as Video;
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index cc5e7aef9..b8b603245 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -12,6 +12,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -31,19 +32,17 @@ namespace MediaBrowser.Providers.MediaInfo
_fileSystem = fileSystem;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType> { ImageType.Primary };
}
- public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var audio = (Audio)item;
var imageStreams =
- audio.GetMediaSources(false)
- .Take(1)
- .SelectMany(i => i.MediaStreams)
+ audio.GetMediaStreams(MediaStreamType.EmbeddedImage)
.Where(i => i.Type == MediaStreamType.EmbeddedImage)
.ToList();
@@ -94,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo
private string GetAudioImagePath(Audio item)
{
var filename = item.Album ?? string.Empty;
- filename += string.Join(",", item.Artists.ToArray());
+ filename += string.Join(",", item.Artists.ToArray(item.Artists.Count));
if (!string.IsNullOrWhiteSpace(item.Album))
{
@@ -129,7 +128,7 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Image Extractor"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
var audio = item as Audio;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index 04e549526..b0785298b 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
@@ -13,6 +13,7 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using System.Linq;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -95,14 +96,14 @@ namespace MediaBrowser.Providers.MediaInfo
{
var mediaStreams = mediaInfo.MediaStreams;
- //audio.FormatName = mediaInfo.Container;
+ audio.Container = mediaInfo.Container;
audio.TotalBitrate = mediaInfo.Bitrate;
audio.RunTimeTicks = mediaInfo.RunTimeTicks;
audio.Size = mediaInfo.Size;
var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.');
- audio.Container = extension;
+ //audio.Container = extension;
await FetchDataFromTags(audio, mediaInfo).ConfigureAwait(false);
@@ -165,12 +166,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!audio.LockedFields.Contains(MetadataFields.Studios))
{
- audio.Studios.Clear();
-
- foreach (var studio in data.Studios)
- {
- audio.AddStudio(studio);
- }
+ audio.SetStudios(data.Studios);
}
audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist));
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 17e48a835..333f3d593 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -141,11 +141,6 @@ namespace MediaBrowser.Providers.MediaInfo
return _cachedTask;
}
- if (item.VideoType == VideoType.HdDvd)
- {
- return _cachedTask;
- }
-
if (item.IsPlaceHolder)
{
return _cachedTask;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index eaf3505d1..791a5c57b 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -80,35 +80,45 @@ namespace MediaBrowser.Providers.MediaInfo
try
{
- if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
- {
- var inputPath = isoMount != null ? isoMount.MountedPath : item.Path;
+ List<string> streamFileNames = null;
- blurayDiscInfo = GetBDInfo(inputPath);
+ if (item.VideoType == VideoType.Iso)
+ {
+ item.IsoType = DetermineIsoType(isoMount);
}
- OnPreFetch(item, isoMount, blurayDiscInfo);
-
- // If we didn't find any satisfying the min length, just take them all
if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
{
- if (item.PlayableStreamFileNames.Count == 0)
+ streamFileNames = FetchFromDvdLib(item, isoMount);
+
+ if (streamFileNames.Count == 0)
{
_logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
return ItemUpdateType.MetadataImport;
}
}
- if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
+ else if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
{
- if (item.PlayableStreamFileNames.Count == 0)
+ var inputPath = isoMount != null ? isoMount.MountedPath : item.Path;
+
+ blurayDiscInfo = GetBDInfo(inputPath);
+
+ streamFileNames = blurayDiscInfo.Files;
+
+ if (streamFileNames.Count == 0)
{
_logger.Error("No playable vobs found in bluray structure, skipping ffprobe.");
return ItemUpdateType.MetadataImport;
}
}
- var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false);
+ if (streamFileNames == null)
+ {
+ streamFileNames = new List<string>();
+ }
+
+ var result = await GetMediaInfo(item, isoMount, streamFileNames, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -126,10 +136,9 @@ namespace MediaBrowser.Providers.MediaInfo
return ItemUpdateType.MetadataImport;
}
- private const string SchemaVersion = "6";
-
- private async Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
+ private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
IIsoMount isoMount,
+ List<string> streamFileNames,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -138,9 +147,9 @@ namespace MediaBrowser.Providers.MediaInfo
? MediaProtocol.Http
: MediaProtocol.File;
- var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ return _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
- PlayableStreamFileNames = item.PlayableStreamFileNames,
+ PlayableStreamFileNames = streamFileNames,
MountedIso = isoMount,
ExtractChapters = true,
VideoType = item.VideoType,
@@ -148,12 +157,7 @@ namespace MediaBrowser.Providers.MediaInfo
InputPath = item.Path,
Protocol = protocol
- }, cancellationToken).ConfigureAwait(false);
-
- //Directory.CreateDirectory(_fileSystem.GetDirectoryName(cachePath));
- //_json.SerializeToFile(result, cachePath);
-
- return result;
+ }, cancellationToken);
}
protected async Task Fetch(Video video,
@@ -187,6 +191,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
video.Container = null;
}
+ video.Container = mediaInfo.Container;
var chapters = mediaInfo.Chapters ?? new List<ChapterInfo>();
if (blurayInfo != null)
@@ -229,16 +234,9 @@ namespace MediaBrowser.Providers.MediaInfo
extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
}
- await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions
- {
- Chapters = chapters,
- Video = video,
- ExtractImages = extractDuringScan,
- SaveChapters = false
-
- }, cancellationToken).ConfigureAwait(false);
+ await _encodingManager.RefreshChapterImages(video, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);
- await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
+ await _chapterManager.SaveChapters(video.Id.ToString(), chapters).ConfigureAwait(false);
}
}
@@ -265,7 +263,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var video = (Video)item;
- video.PlayableStreamFileNames = blurayInfo.Files.ToList();
+ //video.PlayableStreamFileNames = blurayInfo.Files.ToList();
// Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output
if (blurayInfo.Files.Count > 1)
@@ -371,14 +369,9 @@ namespace MediaBrowser.Providers.MediaInfo
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios))
{
- if (video.Studios.Count == 0 || isFullRefresh)
+ if (video.Studios.Length == 0 || isFullRefresh)
{
- video.Studios.Clear();
-
- foreach (var studio in data.Studios)
- {
- video.AddStudio(studio);
- }
+ video.SetStudios(data.Studios);
}
}
@@ -513,7 +506,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToList();
+ video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToArray();
currentStreams.AddRange(externalSubtitleStreams);
}
@@ -558,31 +551,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- /// <summary>
- /// Called when [pre fetch].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="mount">The mount.</param>
- /// <param name="blurayDiscInfo">The bluray disc information.</param>
- private void OnPreFetch(Video item, IIsoMount mount, BlurayDiscInfo blurayDiscInfo)
- {
- if (item.VideoType == VideoType.Iso)
- {
- item.IsoType = DetermineIsoType(mount);
- }
-
- if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
- {
- FetchFromDvdLib(item, mount);
- }
-
- if (blurayDiscInfo != null)
- {
- item.PlayableStreamFileNames = blurayDiscInfo.Files.ToList();
- }
- }
-
- private void FetchFromDvdLib(Video item, IIsoMount mount)
+ private List<string> FetchFromDvdLib(Video item, IIsoMount mount)
{
var path = mount == null ? item.Path : mount.MountedPath;
var dvd = new Dvd(path, _fileSystem);
@@ -597,7 +566,7 @@ namespace MediaBrowser.Providers.MediaInfo
item.RunTimeTicks = GetRuntime(primaryTitle);
}
- item.PlayableStreamFileNames = GetPrimaryPlaylistVobFiles(item, mount, titleNumber)
+ return GetPrimaryPlaylistVobFiles(item, mount, titleNumber)
.Select(Path.GetFileName)
.ToList();
}
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index dfa705126..465c417e7 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -133,7 +133,7 @@ namespace MediaBrowser.Providers.MediaInfo
throw new ArgumentException(string.Format("Cannot search for items that don't have a path: {0} {1}", video.Name, video.Id));
}
- var files = directoryService.GetFilePaths(containingPath, clearCache);
+ var files = fileSystem.GetFilePaths(containingPath, clearCache);
var videoFileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(video.Path);
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 200615a5d..61cf1cfc3 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -17,6 +17,7 @@ using System.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -83,7 +84,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
MediaTypes = new string[] { MediaType.Video },
IsVirtualItem = false,
- IncludeItemTypes = types.ToArray(),
+ IncludeItemTypes = types.ToArray(types.Count),
DtoOptions = new DtoOptions(true)
}).OfType<Video>()
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index ca701b70f..f666d8b7f 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -18,61 +18,34 @@ namespace MediaBrowser.Providers.MediaInfo
{
public class VideoImageProvider : IDynamicImageProvider, IHasItemChangeMonitor, IHasOrder
{
- private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
- public VideoImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
+ public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger logger, IFileSystem fileSystem)
{
- _isoManager = isoManager;
_mediaEncoder = mediaEncoder;
- _config = config;
- _libraryManager = libraryManager;
_logger = logger;
_fileSystem = fileSystem;
}
- /// <summary>
- /// The null mount task result
- /// </summary>
- protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null);
-
- /// <summary>
- /// Mounts the iso if needed.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IIsoMount}.</returns>
- protected Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken)
- {
- if (item.VideoType == VideoType.Iso)
- {
- return _isoManager.Mount(item.Path, cancellationToken);
- }
-
- return NullMountTaskResult;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType> { ImageType.Primary };
}
- public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var video = (Video)item;
// No support for this
- if (video.VideoType == VideoType.HdDvd || video.IsPlaceHolder)
+ if (video.IsPlaceHolder)
{
return Task.FromResult(new DynamicImageResponse { HasImage = false });
}
- // Can't extract from iso's if we weren't unable to determine iso type
- if (video.VideoType == VideoType.Iso && !video.IsoType.HasValue)
+ // No support for this
+ if (video.VideoType == VideoType.Iso || video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay)
{
return Task.FromResult(new DynamicImageResponse { HasImage = false });
}
@@ -89,79 +62,66 @@ namespace MediaBrowser.Providers.MediaInfo
public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationToken cancellationToken)
{
- var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);
-
- try
- {
- var protocol = item.LocationType == LocationType.Remote
- ? MediaProtocol.Http
- : MediaProtocol.File;
+ var protocol = item.LocationType == LocationType.Remote
+ ? MediaProtocol.Http
+ : MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, isoMount, item.PlayableStreamFileNames);
+ var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, null, item.GetPlayableStreamFileNames());
- var mediaStreams =
- item.GetMediaSources(false)
- .Take(1)
- .SelectMany(i => i.MediaStreams)
- .ToList();
+ var mediaStreams =
+ item.GetMediaStreams();
- var imageStreams =
- mediaStreams
- .Where(i => i.Type == MediaStreamType.EmbeddedImage)
- .ToList();
+ var imageStreams =
+ mediaStreams
+ .Where(i => i.Type == MediaStreamType.EmbeddedImage)
+ .ToList();
- var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
- imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
- imageStreams.FirstOrDefault();
+ var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
+ imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
+ imageStreams.FirstOrDefault();
- string extractedImagePath;
+ string extractedImagePath;
- if (imageStream != null)
+ if (imageStream != null)
+ {
+ // Instead of using the raw stream index, we need to use nth video/embedded image stream
+ var videoIndex = -1;
+ foreach (var mediaStream in mediaStreams)
{
- // Instead of using the raw stream index, we need to use nth video/embedded image stream
- var videoIndex = -1;
- foreach (var mediaStream in mediaStreams)
+ if (mediaStream.Type == MediaStreamType.Video ||
+ mediaStream.Type == MediaStreamType.EmbeddedImage)
{
- if (mediaStream.Type == MediaStreamType.Video ||
- mediaStream.Type == MediaStreamType.EmbeddedImage)
- {
- videoIndex++;
- }
- if (mediaStream == imageStream)
- {
- break;
- }
+ videoIndex++;
+ }
+ if (mediaStream == imageStream)
+ {
+ break;
}
-
- extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, videoIndex, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in.
- // Always use 10 seconds for dvd because our duration could be out of whack
- var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks.HasValue &&
- item.RunTimeTicks.Value > 0
- ? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
- : TimeSpan.FromSeconds(10);
-
- extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
}
- return new DynamicImageResponse
- {
- Format = ImageFormat.Jpg,
- HasImage = true,
- Path = extractedImagePath,
- Protocol = MediaProtocol.File
- };
+ extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
}
- finally
+ else
{
- if (isoMount != null)
- {
- isoMount.Dispose();
- }
+ // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in.
+ // Always use 10 seconds for dvd because our duration could be out of whack
+ var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks.HasValue &&
+ item.RunTimeTicks.Value > 0
+ ? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
+ : TimeSpan.FromSeconds(10);
+
+ var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+
+ extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
}
+
+ return new DynamicImageResponse
+ {
+ Format = ImageFormat.Jpg,
+ HasImage = true,
+ Path = extractedImagePath,
+ Protocol = MediaProtocol.File
+ };
}
public string Name
@@ -169,7 +129,7 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Screen Grabber"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
var video = item as Video;
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index c3aa87a22..0214788ab 100644
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -60,12 +60,12 @@ namespace MediaBrowser.Providers.Movies
get { return "FanArt"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Movie || item is BoxSet || item is MusicVideo;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var baseItem = (BaseItem)item;
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
index b40e4a120..65742f6e6 100644
--- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
+++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
@@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Movies
movie.ProductionLocations = movieData
.production_countries
.Select(i => i.name)
- .ToList();
+ .ToArray(movieData.production_countries.Count);
}
movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
@@ -213,12 +213,7 @@ namespace MediaBrowser.Providers.Movies
//studios
if (movieData.production_companies != null)
{
- movie.Studios.Clear();
-
- foreach (var studio in movieData.production_companies.Select(c => c.name))
- {
- movie.AddStudio(studio);
- }
+ movie.SetStudios(movieData.production_companies.Select(c => c.name));
}
// genres
@@ -307,10 +302,10 @@ namespace MediaBrowser.Providers.Movies
}
}
- if (movieData.keywords != null && movieData.keywords.keywords != null)
- {
- movie.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
- }
+ //if (movieData.keywords != null && movieData.keywords.keywords != null)
+ //{
+ // movie.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
+ //}
if (movieData.trailers != null && movieData.trailers.youtube != null &&
movieData.trailers.youtube.Count > 0)
@@ -323,7 +318,7 @@ namespace MediaBrowser.Providers.Movies
Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
Name = i.name
- }).ToList();
+ }).ToArray();
}
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index 215b39181..8dc4bd56a 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -41,12 +41,12 @@ namespace MediaBrowser.Providers.Movies
get { return "TheMovieDb"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Movie || item is MusicVideo || item is Trailer;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -55,7 +55,7 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -127,8 +127,7 @@ namespace MediaBrowser.Providers.Movies
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
/// <summary>
@@ -149,8 +148,7 @@ namespace MediaBrowser.Providers.Movies
private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images)
{
var eligibleBackdrops = images.backdrops == null ? new List<MovieDbProvider.Backdrop>() :
- images.backdrops
- .ToList();
+ images.backdrops;
return eligibleBackdrops.OrderByDescending(i => i.vote_average)
.ThenByDescending(i => i.vote_count);
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index fe190afb4..ec1c49742 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Common;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Movies
{
@@ -282,7 +283,7 @@ namespace MediaBrowser.Providers.Movies
languages.Add("en");
}
- return string.Join(",", languages.ToArray());
+ return string.Join(",", languages.ToArray(languages.Count));
}
public static string NormalizeLanguage(string language)
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index 333d289ec..dc3146f2a 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Movies
return base.IsFullLocalMetadata(item);
}
- protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Movies
return base.IsFullLocalMetadata(item);
}
- protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs b/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs
index 0cb1a7ff1..57e20b852 100644
--- a/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs
+++ b/MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs
@@ -11,12 +11,12 @@ namespace MediaBrowser.Providers.Music
{
public class AlbumImageFromSongProvider : IDynamicImageProvider
{
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType> { ImageType.Primary };
}
- public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
{
var album = (MusicAlbum)item;
@@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.Music
get { return "Image Extractor"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is MusicAlbum;
}
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index bba40608c..fe39a841e 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -44,13 +44,13 @@ namespace MediaBrowser.Providers.Music
if (!item.LockedFields.Contains(MetadataFields.Studios))
{
- var currentList = item.Studios.ToList();
+ var currentList = item.Studios;
item.Studios = songs.SelectMany(i => i.Studios)
.Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
+ .ToArray();
- if (currentList.Count != item.Studios.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ if (currentList.Length != item.Studios.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
{
updateType = updateType | ItemUpdateType.MetadataEdit;
}
@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Music
.SelectMany(i => i.AlbumArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
- .ToList();
+ .ToArray();
if (!item.AlbumArtists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
{
@@ -151,7 +151,7 @@ namespace MediaBrowser.Providers.Music
return updateType;
}
- protected override void MergeData(MetadataResult<MusicAlbum> source, MetadataResult<MusicAlbum> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicAlbum> source, MetadataResult<MusicAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index 98fe766e0..c8008ba1e 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Music
Recursive = true,
IsFolder = false
}) :
- item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToList();
+ item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
var currentList = item.Genres.ToList();
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.Music
return updateType;
}
- protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
index c13d19c43..0ce221b06 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
_json = json;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -34,7 +34,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@@ -105,7 +105,7 @@ namespace MediaBrowser.Providers.Music
}
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is MusicAlbum;
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
index cd6b0b827..80f122402 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
@@ -74,7 +74,7 @@ namespace MediaBrowser.Providers.Music
{
if (!string.IsNullOrWhiteSpace(result.strArtist))
{
- item.AlbumArtists = new List<string> { result.strArtist };
+ item.AlbumArtists = new string[] { result.strArtist };
}
if (!string.IsNullOrEmpty(result.intYearReleased))
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
index 6ca1d83d8..dded509e2 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
_httpClient = httpClient;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
@@ -138,7 +138,7 @@ namespace MediaBrowser.Providers.Music
get { return "TheAudioDB"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is MusicArtist;
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index 4d791be01..68a4fcd7c 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -8,12 +8,13 @@ using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Music
{
public class AudioMetadataService : MetadataService<Audio, SongInfo>
{
- protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 5700b2386..7a6826d31 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -47,12 +47,12 @@ namespace MediaBrowser.Providers.Music
get { return "FanArt"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is MusicAlbum;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var album = (MusicAlbum)item;
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index eefebed29..6094fd26e 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -58,12 +58,12 @@ namespace MediaBrowser.Providers.Music
get { return "FanArt"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is MusicArtist;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var artist = (MusicArtist)item;
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index b77fcf1b2..a35fd696a 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -17,6 +17,7 @@ using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Music
{
@@ -123,6 +124,7 @@ namespace MediaBrowser.Providers.Music
}
return result;
+
}).ToList();
}
}
diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
index 1a2b13e94..06f60c8a3 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Music
using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
{
- var results = GetResultsFromResponse(stream).ToList();
+ var results = GetResultsFromResponse(stream);
if (results.Count > 0)
{
@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Music
return new List<RemoteSearchResult>();
}
- private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
+ private List<RemoteSearchResult> GetResultsFromResponse(Stream stream)
{
using (var oReader = new StreamReader(stream, Encoding.UTF8))
{
@@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.Music
}
}
- private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
+ private List<RemoteSearchResult> ParseArtistList(XmlReader reader)
{
var list = new List<RemoteSearchResult>();
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index b8f6e2c63..27c6deb74 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -10,12 +10,13 @@ using System.Linq;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Music
{
class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
{
- protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
index ba059bd7b..3828f8d27 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.MusicGenres
{
public class MusicGenreMetadataService : MetadataService<MusicGenre, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
index f8b3ba155..74bfa6e03 100644
--- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Omdb
_configurationManager = configurationManager;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -40,7 +40,7 @@ namespace MediaBrowser.Providers.Omdb
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
@@ -91,7 +91,7 @@ namespace MediaBrowser.Providers.Omdb
get { return "The Open Movie Database"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Movie || item is Trailer || item is Episode;
}
diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
index 372813790..4dfcdba09 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
@@ -37,12 +37,12 @@ namespace MediaBrowser.Providers.People
get { return "TheMovieDb"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Person;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.People
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var person = (Person)item;
var id = person.GetProviderId(MetadataProviders.Tmdb);
@@ -116,8 +116,7 @@ namespace MediaBrowser.Providers.People
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
private string GetLanguage(MovieDbPersonProvider.Profile profile)
diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
index 986c4b4a9..d8c9ce801 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
@@ -184,7 +184,7 @@ namespace MediaBrowser.Providers.People
if (!string.IsNullOrWhiteSpace(info.place_of_birth))
{
- item.ProductionLocations = new List<string> { info.place_of_birth };
+ item.ProductionLocations = new string[] { info.place_of_birth };
}
item.Overview = info.biography;
diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs
index 5d70fc1d6..bbfaa43b4 100644
--- a/MediaBrowser.Providers/People/PersonMetadataService.cs
+++ b/MediaBrowser.Providers/People/PersonMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.People
{
public class PersonMetadataService : MetadataService<Person, PersonLookupInfo>
{
- protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
index efbf46cd6..2c3ba00c8 100644
--- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
@@ -48,12 +48,12 @@ namespace MediaBrowser.Providers.People
get { return "TheTVDB"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Person;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.People
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery
{
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index 85b9dafd4..bff933ccf 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Photos
{
class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index 909d359b9..d7f4982e4 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Providers.Photos
{
class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index c4ea106fb..dccef3a09 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Playlists
{
class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
index ef5ced3e2..23953b0d3 100644
--- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs
+++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Providers.Studios
{
public class StudioMetadataService : MetadataService<Studio, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 8579bd16b..f63615430 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -37,12 +37,12 @@ namespace MediaBrowser.Providers.Studios
get { return "Emby Designs"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Studio;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -51,12 +51,12 @@ namespace MediaBrowser.Providers.Studios
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken)
+ private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, bool posters, bool thumbs, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -83,7 +83,7 @@ namespace MediaBrowser.Providers.Studios
return list.Where(i => i != null);
}
- private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
+ private RemoteImageInfo GetImage(IHasMetadata item, string filename, ImageType type, string remoteFilename)
{
var list = ImageUtils.GetAvailableImages(filename, _fileSystem);
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index f7071f519..fce95364e 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -295,8 +295,7 @@ namespace MediaBrowser.Providers.Subtitles
}
var providers = _subtitleProviders
- .Where(i => i.SupportedMediaTypes.Contains(mediaType))
- .ToList();
+ .Where(i => i.SupportedMediaTypes.Contains(mediaType));
return providers.Select(i => new SubtitleProviderInfo
{
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 8adb6d4d2..29533a46b 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.TV
return updateType;
}
- protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
index b9f010bb6..002c98e95 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
@@ -50,12 +50,12 @@ namespace MediaBrowser.Providers.TV
get { return "FanArt"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Season;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
index 644e9cbb5..583e5900d 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
@@ -59,12 +59,12 @@ namespace MediaBrowser.Providers.TV
get { return "FanArt"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -77,7 +77,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 51ad12364..45e10a76e 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -329,7 +329,7 @@ namespace MediaBrowser.Providers.TV
return true;
}
- if (!allowMissingEpisodes && i.Episode.IsMissingEpisode)
+ if (!allowMissingEpisodes && i.Episode.IsMissingEpisode && !i.Episode.IsUnaired)
{
return true;
}
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
index daf493ad9..48af89830 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.TV
};
// Allowing this will dramatically increase scan times
- if (info.IsMissingEpisode || info.IsVirtualUnaired)
+ if (info.IsMissingEpisode)
{
return result;
}
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index 1b4e3f44f..c10b0ef9d 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.TV
return updateType;
}
- protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index 7559a15de..3977ba9d9 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -57,7 +57,7 @@ namespace MediaBrowser.Providers.TV
return base.IsFullLocalMetadata(item);
}
- protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@@ -74,7 +74,7 @@ namespace MediaBrowser.Providers.TV
targetItem.Status = sourceItem.Status;
}
- if (replaceData || targetItem.AirDays == null || targetItem.AirDays.Count == 0)
+ if (replaceData || targetItem.AirDays == null || targetItem.AirDays.Length == 0)
{
targetItem.AirDays = sourceItem.AirDays;
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
index 854f9f06b..728bbc505 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.TV
: base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, logManager)
{}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var episode = (Controller.Entities.TV.Episode)item;
var series = episode.Series;
@@ -104,8 +104,7 @@ namespace MediaBrowser.Providers.TV
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
@@ -125,7 +124,7 @@ namespace MediaBrowser.Providers.TV
get { return "TheMovieDb"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Controller.Entities.TV.Episode;
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
index 618a4df45..31785ca9c 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
@@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.TV
var result = new MetadataResult<Episode>();
// Allowing this will dramatically increase scan times
- if (info.IsMissingEpisode || info.IsVirtualUnaired)
+ if (info.IsMissingEpisode)
{
return result;
}
@@ -134,7 +134,7 @@ namespace MediaBrowser.Providers.TV
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
{
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
- item.AddTrailerUrl(videoUrl, true);
+ item.AddTrailerUrl(videoUrl);
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
index c746e0488..f05f7bb30 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
@@ -40,12 +40,12 @@ namespace MediaBrowser.Providers.TV
get { return "TheMovieDb"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -54,7 +54,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -118,8 +118,7 @@ namespace MediaBrowser.Providers.TV
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
/// <summary>
@@ -138,8 +137,7 @@ namespace MediaBrowser.Providers.TV
private IEnumerable<MovieDbSeriesProvider.Backdrop> GetBackdrops(MovieDbSeriesProvider.Images images)
{
var eligibleBackdrops = images.backdrops == null ? new List<MovieDbSeriesProvider.Backdrop>() :
- images.backdrops
- .ToList();
+ images.backdrops;
return eligibleBackdrops.OrderByDescending(i => i.vote_average)
.ThenByDescending(i => i.vote_count);
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
index 0b5708b56..72745a9c3 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
@@ -21,6 +21,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.TV
{
@@ -231,7 +232,7 @@ namespace MediaBrowser.Providers.TV
if (seriesInfo.networks != null)
{
- series.Studios = seriesInfo.networks.Select(i => i.name).ToList();
+ series.Studios = seriesInfo.networks.Select(i => i.name).ToArray(seriesInfo.networks.Count);
}
if (seriesInfo.genres != null)
@@ -301,7 +302,7 @@ namespace MediaBrowser.Providers.TV
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
{
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
- series.AddTrailerUrl(videoUrl, true);
+ series.AddTrailerUrl(videoUrl);
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
index 48e50c2e1..7b6bcc4c7 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
@@ -35,12 +35,12 @@ namespace MediaBrowser.Providers.TV
get { return "TheTVDB"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Episode;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var episode = (Episode)item;
var series = episode.Series;
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
index 9a457ba94..30d2691e3 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
@@ -320,9 +320,8 @@ namespace MediaBrowser.Providers.TV
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task UpdateSeries(IEnumerable<string> seriesIds, string seriesDataPath, long? lastTvDbUpdateTime, IProgress<double> progress, CancellationToken cancellationToken)
+ private async Task UpdateSeries(List<string> seriesIds, string seriesDataPath, long? lastTvDbUpdateTime, IProgress<double> progress, CancellationToken cancellationToken)
{
- var list = seriesIds.ToList();
var numComplete = 0;
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
@@ -342,7 +341,7 @@ namespace MediaBrowser.Providers.TV
.Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)))
.ToLookup(i => i.GetProviderId(MetadataProviders.Tvdb));
- foreach (var seriesId in list)
+ foreach (var seriesId in seriesIds)
{
// Find the preferred language(s) for the movie in the library
var languages = allSeries[seriesId]
@@ -371,7 +370,7 @@ namespace MediaBrowser.Providers.TV
numComplete++;
double percent = numComplete;
- percent /= list.Count;
+ percent /= seriesIds.Count;
percent *= 100;
progress.Report(percent);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
index 395d419cf..22dab1cd7 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
@@ -48,12 +48,12 @@ namespace MediaBrowser.Providers.TV
get { return "TheTVDB"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Season;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -63,7 +63,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var season = (Season)item;
var series = season.Series;
@@ -175,8 +175,7 @@ namespace MediaBrowser.Providers.TV
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
private static void AddImage(XmlReader reader, List<RemoteImageInfo> images, int seasonNumber)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
index d7eea4226..6f3d763e2 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
@@ -49,12 +49,12 @@ namespace MediaBrowser.Providers.TV
get { return "TheTVDB"; }
}
- public bool Supports(IHasImages item)
+ public bool Supports(IHasMetadata item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new List<ImageType>
{
@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
if (TvdbSeriesProvider.IsValidSeries(item.ProviderIds))
{
@@ -169,8 +169,7 @@ namespace MediaBrowser.Providers.TV
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
- .ThenByDescending(i => i.VoteCount ?? 0)
- .ToList();
+ .ThenByDescending(i => i.VoteCount ?? 0);
}
private void AddImage(XmlReader reader, List<RemoteImageInfo> images)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 8a10affb9..d447aff99 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -482,7 +482,7 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task{System.String}.</returns>
private async Task<IEnumerable<RemoteSearchResult>> FindSeries(string name, int? year, string language, CancellationToken cancellationToken)
{
- var results = (await FindSeriesInternal(name, language, cancellationToken).ConfigureAwait(false)).ToList();
+ var results = (await FindSeriesInternal(name, language, cancellationToken).ConfigureAwait(false));
if (results.Count == 0)
{
@@ -491,7 +491,7 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrWhiteSpace(nameWithoutYear) && !string.Equals(nameWithoutYear, name, StringComparison.OrdinalIgnoreCase))
{
- results = (await FindSeriesInternal(nameWithoutYear, language, cancellationToken).ConfigureAwait(false)).ToList();
+ results = (await FindSeriesInternal(nameWithoutYear, language, cancellationToken).ConfigureAwait(false));
}
}
@@ -507,7 +507,7 @@ namespace MediaBrowser.Providers.TV
});
}
- private async Task<IEnumerable<RemoteSearchResult>> FindSeriesInternal(string name, string language, CancellationToken cancellationToken)
+ private async Task<List<RemoteSearchResult>> FindSeriesInternal(string name, string language, CancellationToken cancellationToken)
{
var url = string.Format(SeriesSearchUrl, WebUtility.UrlEncode(name), NormalizeLanguage(language));
var searchResults = new List<RemoteSearchResult>();
@@ -1091,26 +1091,26 @@ namespace MediaBrowser.Providers.TV
}
case "Airs_DayOfWeek":
- {
- var val = reader.ReadElementContentAsString();
+ {
+ var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.AirDays = TVUtils.GetAirDays(val);
- }
- break;
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirDays = TVUtils.GetAirDays(val);
}
+ break;
+ }
case "Airs_Time":
- {
- var val = reader.ReadElementContentAsString();
+ {
+ var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.AirTime = val;
- }
- break;
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirTime = val;
}
+ break;
+ }
case "ContentRating":
{
@@ -1273,12 +1273,7 @@ namespace MediaBrowser.Providers.TV
if (vals.Count > 0)
{
- item.Studios.Clear();
-
- foreach (var genre in vals)
- {
- item.AddStudio(genre);
- }
+ item.SetStudios(vals);
}
}
diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs
index d2860a622..104bcb5ae 100644
--- a/MediaBrowser.Providers/TV/TvExternalIds.cs
+++ b/MediaBrowser.Providers/TV/TvExternalIds.cs
@@ -96,27 +96,4 @@ namespace MediaBrowser.Providers.TV
return item is Episode;
}
}
-
- public class TvComSeriesExternalId : IExternalId
- {
- public string Name
- {
- get { return "TV.com"; }
- }
-
- public string Key
- {
- get { return MetadataProviders.Tvcom.ToString(); }
- }
-
- public string UrlFormatString
- {
- get { return null; }
- }
-
- public bool Supports(IHasProviderIds item)
- {
- return item is Series;
- }
- }
}
diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs
index 88df704d6..694315c18 100644
--- a/MediaBrowser.Providers/Users/UserMetadataService.cs
+++ b/MediaBrowser.Providers/Users/UserMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Users
{
public class UserMetadataService : MetadataService<User, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
index a07cc5949..f493eb31f 100644
--- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs
+++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Videos
}
}
- protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs
index 36c2fd1dd..783833656 100644
--- a/MediaBrowser.Providers/Years/YearMetadataService.cs
+++ b/MediaBrowser.Providers/Years/YearMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Years
{
public class YearMetadataService : MetadataService<Year, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index 196bdd096..cbf30db8e 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -93,7 +93,7 @@
<HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
+ <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
@@ -116,10 +116,10 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
</Reference>
<Reference Include="Emby.Server.Connect">
<HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
index df98c6584..48b4fdb4f 100644
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -52,8 +52,7 @@
<HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
@@ -71,12 +70,10 @@
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="MediaBrowser.IsoMounting.Linux">
@@ -133,10 +130,6 @@
<Project>{89ab4548-770d-41fd-a891-8daff44f452c}</Project>
<Name>Emby.Photos</Name>
</ProjectReference>
- <ProjectReference Include="..\Emby.Server.Core\Emby.Server.Core.csproj">
- <Project>{776b9f0c-5195-45e3-9a36-1cc1f0d8e0b0}</Project>
- <Name>Emby.Server.Core</Name>
- </ProjectReference>
<ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj">
<Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project>
<Name>Emby.Server.Implementations</Name>
@@ -169,10 +162,6 @@
<Project>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</Project>
<Name>MediaBrowser.Api</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
<Project>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</Project>
<Name>MediaBrowser.LocalMetadata</Name>
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index 0a70c446f..aa6a58b48 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -16,8 +16,6 @@ using Emby.Common.Implementations.Logging;
using Emby.Common.Implementations.Networking;
using Emby.Server.Core.Cryptography;
using Emby.Server.Core;
-using Emby.Server.Core.IO;
-using Emby.Server.Core.Logging;
using Emby.Server.Implementations;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Logging;
@@ -115,7 +113,7 @@ namespace MediaBrowser.Server.Mono
"emby.mono.zip",
environmentInfo,
imageEncoder,
- new Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
+ new SystemEvents(logManager.GetLogger("SystemEvents")),
new MemoryStreamProvider(),
new NetworkManager(logManager.GetLogger("NetworkManager")),
GenerateCertificate,
diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config
index dcc477e9a..c77327e22 100644
--- a/MediaBrowser.Server.Mono/packages.config
+++ b/MediaBrowser.Server.Mono/packages.config
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
- <package id="NLog" version="4.4.11" targetFramework="net46" />
+ <package id="NLog" version="4.4.12" targetFramework="net46" />
<package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
<package id="SharpCompress" version="0.14.0" targetFramework="net46" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net46" />
- <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net46" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.8" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index bc38476ca..e687b34cf 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -24,14 +24,13 @@ using Emby.Common.Implementations.Networking;
using Emby.Server.Core.Cryptography;
using Emby.Drawing;
using Emby.Server.Core;
-using Emby.Server.Core.IO;
-using Emby.Server.Core.Logging;
using Emby.Server.Implementations;
using Emby.Server.Implementations.Browser;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Logging;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
+using SystemEvents = Emby.Server.Implementations.SystemEvents;
namespace MediaBrowser.ServerApplication
{
@@ -320,8 +319,8 @@ namespace MediaBrowser.ServerApplication
"emby.windows.zip",
environmentInfo,
new NullImageEncoder(),
- new Server.Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
- new RecyclableMemoryStreamProvider(),
+ new SystemEvents(logManager.GetLogger("SystemEvents")),
+ new MemoryStreamProvider(),
new Networking.NetworkManager(logManager.GetLogger("NetworkManager")),
GenerateCertificate,
() => Environment.UserDomainName);
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 591ac0fba..a4138a57d 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -74,8 +74,7 @@
<HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
@@ -92,12 +91,10 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
- <Private>True</Private>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@@ -226,10 +223,6 @@
<Project>{89ab4548-770d-41fd-a891-8daff44f452c}</Project>
<Name>Emby.Photos</Name>
</ProjectReference>
- <ProjectReference Include="..\Emby.Server.Core\Emby.Server.Core.csproj">
- <Project>{776b9f0c-5195-45e3-9a36-1cc1f0d8e0b0}</Project>
- <Name>Emby.Server.Core</Name>
- </ProjectReference>
<ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj">
<Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project>
<Name>Emby.Server.Implementations</Name>
@@ -250,10 +243,6 @@
<Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project>
<Name>MediaBrowser.LocalMetadata</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0bd82fa6-eb8a-4452-8af5-74f9c3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index 0fa93db82..ed953e299 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="NLog" version="4.4.11" targetFramework="net462" />
+ <package id="NLog" version="4.4.12" targetFramework="net462" />
<package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
<package id="SharpCompress" version="0.14.0" targetFramework="net462" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net462" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net462" />
- <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net462" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net462" />
+ <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net462" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.8" targetFramework="net462" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Tests/MediaBrowser.Tests.csproj b/MediaBrowser.Tests/MediaBrowser.Tests.csproj
index a031d47ea..57a645ce0 100644
--- a/MediaBrowser.Tests/MediaBrowser.Tests.csproj
+++ b/MediaBrowser.Tests/MediaBrowser.Tests.csproj
@@ -37,6 +37,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="Emby.Server.MediaEncoding">
+ <HintPath>..\ThirdParty\emby\Emby.Server.MediaEncoding.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.XML" />
</ItemGroup>
@@ -69,10 +72,6 @@
<Project>{1e37a338-9f57-4b70-bd6d-bb9c591e319b}</Project>
<Name>Emby.Common.Implementations</Name>
</ProjectReference>
- <ProjectReference Include="..\Emby.Server.Core\Emby.Server.Core.csproj">
- <Project>{776b9f0c-5195-45e3-9a36-1cc1f0d8e0b0}</Project>
- <Name>Emby.Server.Core</Name>
- </ProjectReference>
<ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj">
<Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project>
<Name>Emby.Server.Implementations</Name>
@@ -85,10 +84,6 @@
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
@@ -108,20 +103,10 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
- </ItemGroup>
- <ItemGroup>
- <None Include="MediaEncoding\Subtitles\TestSubtitles\expected.vtt">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="MediaEncoding\Subtitles\TestSubtitles\data.ass">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="MediaEncoding\Subtitles\TestSubtitles\data2.ass">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="MediaEncoding\Subtitles\TestSubtitles\unit.srt">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
+ <None Include="MediaEncoding\Subtitles\TestSubtitles\data.ass" />
+ <None Include="MediaEncoding\Subtitles\TestSubtitles\data2.ass" />
+ <None Include="MediaEncoding\Subtitles\TestSubtitles\expected.vtt" />
+ <None Include="MediaEncoding\Subtitles\TestSubtitles\unit.srt" />
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="ConsistencyTests\Resources\StringCheck.xslt">
diff --git a/MediaBrowser.Tests/MediaEncoding/Subtitles/AssParserTests.cs b/MediaBrowser.Tests/MediaEncoding/Subtitles/AssParserTests.cs
index d5a62802d..47f5891c6 100644
--- a/MediaBrowser.Tests/MediaEncoding/Subtitles/AssParserTests.cs
+++ b/MediaBrowser.Tests/MediaEncoding/Subtitles/AssParserTests.cs
@@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using Emby.Server.MediaEncoding.Subtitles;
namespace MediaBrowser.Tests.MediaEncoding.Subtitles {
diff --git a/MediaBrowser.Tests/MediaEncoding/Subtitles/SrtParserTests.cs b/MediaBrowser.Tests/MediaEncoding/Subtitles/SrtParserTests.cs
index af34e13e7..280dc5d78 100644
--- a/MediaBrowser.Tests/MediaEncoding/Subtitles/SrtParserTests.cs
+++ b/MediaBrowser.Tests/MediaEncoding/Subtitles/SrtParserTests.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using MediaBrowser.MediaEncoding.Subtitles;
+using Emby.Server.MediaEncoding.Subtitles;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using Microsoft.VisualStudio.TestTools.UnitTesting;
diff --git a/MediaBrowser.Tests/MediaEncoding/Subtitles/VttWriterTest.cs b/MediaBrowser.Tests/MediaEncoding/Subtitles/VttWriterTest.cs
index 924a1736f..f6e2c5298 100644
--- a/MediaBrowser.Tests/MediaEncoding/Subtitles/VttWriterTest.cs
+++ b/MediaBrowser.Tests/MediaEncoding/Subtitles/VttWriterTest.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using MediaBrowser.MediaEncoding.Subtitles;
+using Emby.Server.MediaEncoding.Subtitles;
using MediaBrowser.Model.MediaInfo;
using Microsoft.VisualStudio.TestTools.UnitTesting;
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index cf3e221b3..c6bbca672 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -174,6 +174,9 @@ namespace MediaBrowser.WebDashboard.Api
IPlugin plugin = null;
Stream stream = null;
+ var isJs = false;
+ var isTemplate = false;
+
var page = ServerEntryPoint.Instance.PluginConfigurationPages.FirstOrDefault(p => string.Equals(p.Name, request.Name, StringComparison.OrdinalIgnoreCase));
if (page != null)
{
@@ -188,11 +191,23 @@ namespace MediaBrowser.WebDashboard.Api
{
plugin = altPage.Item2;
stream = _assemblyInfo.GetManifestResourceStream(plugin.GetType(), altPage.Item1.EmbeddedResourcePath);
+
+ isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase);
+ isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html");
}
}
if (plugin != null && stream != null)
{
+ if (isJs)
+ {
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.js"), () => Task.FromResult(stream));
+ }
+ if (isTemplate)
+ {
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
+ }
+
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator(DashboardUIPath).ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null));
}
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index da43dd63a..c855a8d9b 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.WebDashboard.Api
{
@@ -39,8 +40,6 @@ namespace MediaBrowser.WebDashboard.Api
if (resourceStream != null)
{
- // Don't apply any caching for html pages
- // jQuery ajax doesn't seem to handle if-modified-since correctly
if (IsFormat(virtualPath, "html"))
{
if (IsCoreHtml(virtualPath))
@@ -284,7 +283,7 @@ namespace MediaBrowser.WebDashboard.Api
files.Insert(0, "cordova.js");
}
- var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray();
+ var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray(files.Count);
builder.Append(string.Join(string.Empty, tags));
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index 188fc8504..ac5313a29 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -56,7 +56,7 @@ namespace MediaBrowser.XbmcMetadata
PersonIds = new [] { person.Id.ToString("N") },
DtoOptions = new DtoOptions(true)
- }).ToList();
+ });
foreach (var item in items)
{
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 3b642eca2..789d840b8 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -316,10 +316,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
- case "type":
- item.DisplayMediaType = reader.ReadElementContentAsString();
- break;
-
case "title":
case "localtitle":
item.Name = reader.ReadElementContentAsString();
@@ -398,13 +394,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "lockedfields":
{
- var fields = new List<MetadataFields>();
-
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
- var list = val.Split('|').Select(i =>
+ item.LockedFields = val.Split('|').Select(i =>
{
MetadataFields field;
@@ -415,13 +409,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return null;
- }).Where(i => i.HasValue).Select(i => i.Value);
-
- fields.AddRange(list);
+ }).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
- item.LockedFields = fields;
-
break;
}
@@ -442,9 +432,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val))
{
- item.ProductionLocations.AddRange(val.Split('/')
+ item.ProductionLocations = val.Split('/')
.Select(i => i.Trim())
- .Where(i => !string.IsNullOrWhiteSpace(i)));
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .ToArray();
}
break;
}
@@ -608,7 +599,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", "https://www.youtube.com/watch?v=", StringComparison.OrdinalIgnoreCase);
- hasTrailer.AddTrailerUrl(val, false);
+ hasTrailer.AddTrailerUrl(val);
}
}
break;
@@ -733,17 +724,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
- case "plotkeyword":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.AddKeyword(val);
- }
- break;
- }
-
case "fileinfo":
{
if (!reader.IsEmptyElement)
@@ -937,17 +917,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
name = reader.ReadElementContentAsString() ?? string.Empty;
break;
- case "type":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- type = val;
- }
- break;
- }
-
case "role":
{
var val = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 035ac15c0..f9ca238c7 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -60,10 +60,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
movie.SetProviderId(MetadataProviders.TmdbCollection, tmdbcolid);
}
- var val = reader.ReadElementContentAsString();
+ var val = reader.ReadInnerXml();
+
if (!string.IsNullOrWhiteSpace(val) && movie != null)
{
- movie.CollectionName = val;
+ // TODO Handle this better later
+ if (val.IndexOf('<') == -1)
+ {
+ movie.CollectionName = val;
+ }
}
break;
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index b0db4e6f3..570a7fed8 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -62,21 +62,21 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
case "airs_dayofweek":
- {
- item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString());
- break;
- }
+ {
+ item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString());
+ break;
+ }
case "airs_time":
- {
- var val = reader.ReadElementContentAsString();
+ {
+ var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.AirTime = val;
- }
- break;
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirTime = val;
}
+ break;
+ }
case "status":
{
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 5015dfbf9..41c376ddf 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -35,7 +35,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
"plot",
"customrating",
"lockdata",
- "type",
"dateadded",
"title",
"rating",
@@ -77,7 +76,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
"style",
"imdbid",
"imdb_id",
- "plotkeyword",
"country",
"audiodbalbumid",
"audiodbartistid",
@@ -301,9 +299,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteStartElement("fileinfo");
writer.WriteStartElement("streamdetails");
- var mediaSource = item.GetMediaSources(false).First();
+ var mediaStreams = item.GetMediaStreams();
- foreach (var stream in mediaSource.MediaStreams)
+ foreach (var stream in mediaStreams)
{
writer.WriteStartElement(stream.Type.ToString().ToLower());
@@ -379,9 +377,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (stream.Type == MediaStreamType.Video)
{
- if (mediaSource.RunTimeTicks.HasValue)
+ var runtimeTicks = ((IHasMetadata) item).RunTimeTicks;
+ if (runtimeTicks.HasValue)
{
- var timespan = TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value);
+ var timespan = TimeSpan.FromTicks(runtimeTicks.Value);
writer.WriteElementString("duration", Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture));
writer.WriteElementString("durationinseconds", Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture));
@@ -486,14 +485,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("lockdata", item.IsLocked.ToString().ToLower());
- if (item.LockedFields.Count > 0)
+ if (item.LockedFields.Length > 0)
{
- writer.WriteElementString("lockedfields", string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()));
- }
-
- if (!string.IsNullOrEmpty(item.DisplayMediaType))
- {
- writer.WriteElementString("type", item.DisplayMediaType);
+ writer.WriteElementString("lockedfields", string.Join("|", item.LockedFields));
}
writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat));
@@ -617,13 +611,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
writtenProviderIds.Add(MetadataProviders.Tmdb.ToString());
}
- var tvcom = item.GetProviderId(MetadataProviders.Tvcom);
- if (!string.IsNullOrEmpty(tvcom))
- {
- writer.WriteElementString("tvcomid", tvcom);
- writtenProviderIds.Add(MetadataProviders.Tvcom.ToString());
- }
-
if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage))
{
writer.WriteElementString("language", item.PreferredMetadataLanguage);
@@ -715,11 +702,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- foreach (var tag in item.Keywords)
- {
- writer.WriteElementString("plotkeyword", tag);
- }
-
var externalId = item.GetProviderId(MetadataProviders.AudioDbArtist);
if (!string.IsNullOrEmpty(externalId))
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index aea3f2c70..70fb4e5fa 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
list.Add(Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo"));
}
- if (!item.IsPlaceHolder && (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd))
+ if (!item.IsPlaceHolder && (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay))
{
var path = item.ContainingFolderPath;
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index e9b2b786a..a2f07d952 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -69,20 +69,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString("status", series.Status.Value.ToString());
}
-
- if (!string.IsNullOrEmpty(series.AirTime))
- {
- writer.WriteElementString("airs_time", series.AirTime);
- }
-
- if (series.AirDays.Count == 7)
- {
- writer.WriteElementString("airs_dayofweek", "Daily");
- }
- else if (series.AirDays.Count > 0)
- {
- writer.WriteElementString("airs_dayofweek", series.AirDays[0].ToString());
- }
}
protected override List<string> GetTagsUsed(IHasMetadata item)
@@ -94,9 +80,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
"episodeguide",
"season",
"episode",
- "status",
- "airs_time",
- "airs_dayofweek"
+ "status"
});
return list;
}
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 1a4638265..382417bb6 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.26403.7
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}"
EndProject
@@ -42,8 +42,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "M
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplication", "MediaBrowser.ServerApplication\MediaBrowser.ServerApplication.csproj", "{94ADE4D3-B7EC-45CD-A200-CC469433072B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}"
@@ -70,8 +68,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Skia", "Emby.Drawing.Skia\Emby.Drawing.Skia.csproj", "{2312DA6D-FF86-4597-9777-BCEEC32D96DD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Core", "Emby.Server.Core\Emby.Server.Core.csproj", "{776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.csproj", "{1E37A338-9F57-4B70-BD6D-BB9C591E319B}"
@@ -394,37 +390,6 @@ Global
{94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|x64.Build.0 = Release|Any CPU
{94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|x86.ActiveCfg = Debug|x86
{94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|x86.Build.0 = Debug|x86
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x64.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Win32.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x64.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Win32.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Win32.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x64.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x64.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.Build.0 = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -905,46 +870,6 @@ Global
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x64.Build.0 = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.ActiveCfg = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|Win32.Build.0 = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|x64.ActiveCfg = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|x64.Build.0 = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Debug|x86.Build.0 = Debug|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|Win32.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|x64.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release Mono|x86.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Any CPU.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Win32.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|Win32.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|x64.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|x64.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|x86.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Release|x86.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Any CPU.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Win32.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|Win32.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|x64.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|x64.Build.0 = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|x86.ActiveCfg = Release|Any CPU
- {776B9F0C-5195-45E3-9A36-1CC1F0D8E0B0}.Signed|x86.Build.0 = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
diff --git a/Mono.Nat/NatUtility.cs b/Mono.Nat/NatUtility.cs
index 983e86833..c1983658d 100644
--- a/Mono.Nat/NatUtility.cs
+++ b/Mono.Nat/NatUtility.cs
@@ -98,16 +98,14 @@ namespace Mono.Nat
{
try
{
- var enabledProtocols = EnabledProtocols.ToList();
-
- if (enabledProtocols.Contains(PmpSearcher.Instance.Protocol))
+ if (EnabledProtocols.Contains(PmpSearcher.Instance.Protocol))
{
await Receive(PmpSearcher.Instance, PmpSearcher.sockets).ConfigureAwait(false);
}
foreach (ISearcher s in controllers)
{
- if (s.NextSearch < DateTime.Now && enabledProtocols.Contains(s.Protocol))
+ if (s.NextSearch < DateTime.Now && EnabledProtocols.Contains(s.Protocol))
{
Log("Searching for: {0}", s.GetType().Name);
s.Search();
diff --git a/Mono.Nat/Pmp/PmpNatDevice.cs b/Mono.Nat/Pmp/PmpNatDevice.cs
index 10ebbdc2c..fb45b365b 100644
--- a/Mono.Nat/Pmp/PmpNatDevice.cs
+++ b/Mono.Nat/Pmp/PmpNatDevice.cs
@@ -31,6 +31,7 @@ using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
namespace Mono.Nat.Pmp
{
@@ -86,7 +87,7 @@ namespace Mono.Nat.Pmp
try
{
- byte[] buffer = package.ToArray();
+ byte[] buffer = package.ToArray(package.Count);
int attempt = 0;
int delay = PmpConstants.RetryDelay;
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
deleted file mode 100644
index fdc2b9f7d..000000000
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
- <metadata>
- <id>MediaBrowser.Common.Internal</id>
- <version>3.0.681</version>
- <title>Emby.Common.Internal</title>
- <authors>Luke</authors>
- <owners>ebr,Luke,scottisafool</owners>
- <projectUrl>https://github.com/MediaBrowser/Emby</projectUrl>
- <iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
- <requireLicenseAcceptance>false</requireLicenseAcceptance>
- <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
- <copyright>Copyright © Emby 2013</copyright>
- <dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.680" />
- <dependency id="NLog" version="4.3.8" />
- <dependency id="SimpleInjector" version="3.2.2" />
- </dependencies>
- </metadata>
- <files>
- <file src="dlls\MediaBrowser.Common.Implementations.dll" target="lib\net45\MediaBrowser.Common.Implementations.dll" />
- <file src="..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll" target="lib\net45\ServiceStack.Text.dll" />
- </files>
-</package> \ No newline at end of file
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 26c19c79a..9be89112a 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.708</version>
+ <version>3.0.729</version>
<title>Emby.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 641db709a..1d2e484db 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.708</version>
+ <version>3.0.729</version>
<title>Emby.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.708" />
+ <dependency id="MediaBrowser.Common" version="3.0.729" />
</dependencies>
</metadata>
<files>
diff --git a/SocketHttpListener/Net/WebHeaderCollection.cs b/SocketHttpListener/Net/WebHeaderCollection.cs
index d20f99b9b..66e159ccb 100644
--- a/SocketHttpListener/Net/WebHeaderCollection.cs
+++ b/SocketHttpListener/Net/WebHeaderCollection.cs
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
namespace SocketHttpListener.Net
{
@@ -176,7 +177,7 @@ namespace SocketHttpListener.Net
}
if (separated != null)
- return separated.ToArray();
+ return separated.ToArray(separated.Count);
}
return values;