aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--BDInfo/BDInfo.csproj81
-rw-r--r--BDInfo/BDInfo.nuget.targets6
-rw-r--r--BDInfo/BDROM.cs8
-rw-r--r--BDInfo/BitVector32.cs308
-rw-r--r--BDInfo/TSPlaylistFile.cs10
-rw-r--r--BDInfo/TSStreamFile.cs2
-rw-r--r--DvdLib/DvdLib.csproj75
-rw-r--r--DvdLib/DvdLib.nuget.targets6
-rw-r--r--DvdLib/Ifo/ProgramChain.cs2
-rw-r--r--Emby.Dlna/Api/DlnaServerService.cs (renamed from MediaBrowser.Api/Dlna/DlnaServerService.cs)147
-rw-r--r--Emby.Dlna/Api/DlnaService.cs (renamed from MediaBrowser.Api/Dlna/DlnaService.cs)16
-rw-r--r--Emby.Dlna/Common/StateVariable.cs5
-rw-r--r--Emby.Dlna/Configuration/DlnaOptions.cs (renamed from MediaBrowser.Model/Configuration/DlnaOptions.cs)4
-rw-r--r--Emby.Dlna/ConfigurationExtension.cs4
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs4
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs10
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs127
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs2
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs157
-rw-r--r--Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs4
-rw-r--r--Emby.Dlna/ControlRequest.cs (renamed from MediaBrowser.Controller/Dlna/ControlRequest.cs)2
-rw-r--r--Emby.Dlna/ControlResponse.cs (renamed from MediaBrowser.Controller/Dlna/ControlResponse.cs)2
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs242
-rw-r--r--Emby.Dlna/DlnaManager.cs35
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj161
-rw-r--r--Emby.Dlna/EventSubscriptionResponse.cs (renamed from MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs)2
-rw-r--r--Emby.Dlna/Eventing/EventManager.cs4
-rw-r--r--Emby.Dlna/IConnectionManager.cs (renamed from MediaBrowser.Controller/Dlna/IConnectionManager.cs)2
-rw-r--r--Emby.Dlna/IContentDirectory.cs (renamed from MediaBrowser.Controller/Dlna/IContentDirectory.cs)2
-rw-r--r--Emby.Dlna/IEventManager.cs (renamed from MediaBrowser.Controller/Dlna/IEventManager.cs)2
-rw-r--r--Emby.Dlna/IMediaReceiverRegistrar.cs (renamed from MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs)2
-rw-r--r--Emby.Dlna/IUpnpService.cs (renamed from MediaBrowser.Controller/Dlna/IUpnpService.cs)2
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs172
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs4
-rw-r--r--Emby.Dlna/PlayTo/Device.cs321
-rw-r--r--Emby.Dlna/PlayTo/DeviceInfo.cs3
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs359
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs116
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItemFactory.cs2
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs13
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs4
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs18
-rw-r--r--Emby.Dlna/Profiles/DenonAvrProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/DirectTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/DishHopperJoeyProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/Foobar2000Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/LgTvProfile.cs16
-rw-r--r--Emby.Dlna/Profiles/LinksysDMA2100Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/MarantzProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/MediaMonkeyProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/PanasonicVieraProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/PopcornHourProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SamsungSmartTvProfile.cs4
-rw-r--r--Emby.Dlna/Profiles/SharpSmartTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2010Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2011Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2012Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2013Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2014Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyPs3Profile.cs3
-rw-r--r--Emby.Dlna/Profiles/SonyPs4Profile.cs3
-rw-r--r--Emby.Dlna/Profiles/WdtvLiveProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/XboxOneProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/Xml/Default.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Denon AVR.xml24
-rw-r--r--Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml13
-rw-r--r--Emby.Dlna/Profiles/Xml/LG Smart TV.xml21
-rw-r--r--Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Marantz.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/MediaMonkey.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Panasonic Viera.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Popcorn Hour.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml17
-rw-r--r--Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml11
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml11
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml11
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml11
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml11
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml7
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml9
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml9
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml9
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml9
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/WDTV Live.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/Xbox One.xml15
-rw-r--r--Emby.Dlna/Profiles/Xml/foobar2000.xml15
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs38
-rw-r--r--Emby.Dlna/Server/UpnpDevice.cs38
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs2
-rw-r--r--Emby.Dlna/Service/ServiceXmlBuilder.cs2
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs64
-rw-r--r--Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj86
-rw-r--r--Emby.Drawing.ImageMagick/ImageMagickEncoder.cs5
-rw-r--r--Emby.Drawing.ImageMagick/packages.config4
-rw-r--r--Emby.Drawing.Net/Emby.Drawing.Net.csproj85
-rw-r--r--Emby.Drawing.Skia/Emby.Drawing.Skia.csproj84
-rw-r--r--Emby.Drawing.Skia/PlayedIndicatorDrawer.cs39
-rw-r--r--Emby.Drawing.Skia/SkiaEncoder.cs66
-rw-r--r--Emby.Drawing.Skia/StripCollageBuilder.cs65
-rw-r--r--Emby.Drawing.Skia/packages.config4
-rw-r--r--Emby.Drawing/Common/ImageHeader.cs21
-rw-r--r--Emby.Drawing/Emby.Drawing.csproj76
-rw-r--r--Emby.Drawing/ImageProcessor.cs185
-rw-r--r--Emby.Naming/Audio/AlbumParser.cs65
-rw-r--r--Emby.Naming/Audio/AudioFileParser.cs23
-rw-r--r--Emby.Naming/Audio/MultiPartResult.cs22
-rw-r--r--Emby.Naming/AudioBook/AudioBookFileInfo.cs49
-rw-r--r--Emby.Naming/AudioBook/AudioBookFilePathParser.cs81
-rw-r--r--Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs13
-rw-r--r--Emby.Naming/AudioBook/AudioBookInfo.cs40
-rw-r--r--Emby.Naming/AudioBook/AudioBookListResolver.cs68
-rw-r--r--Emby.Naming/AudioBook/AudioBookResolver.cs60
-rw-r--r--Emby.Naming/Common/EpisodeExpression.cs45
-rw-r--r--Emby.Naming/Common/MediaType.cs19
-rw-r--r--Emby.Naming/Common/NamingOptions.cs687
-rw-r--r--Emby.Naming/Emby.Naming.csproj12
-rw-r--r--Emby.Naming/Extensions/StringExtensions.cs30
-rw-r--r--Emby.Naming/StringExtensions.cs31
-rw-r--r--Emby.Naming/Subtitles/SubtitleInfo.cs27
-rw-r--r--Emby.Naming/Subtitles/SubtitleParser.cs65
-rw-r--r--Emby.Naming/TV/EpisodeInfo.cs51
-rw-r--r--Emby.Naming/TV/EpisodePathParser.cs223
-rw-r--r--Emby.Naming/TV/EpisodePathParserResult.cs17
-rw-r--r--Emby.Naming/TV/EpisodeResolver.cs76
-rw-r--r--Emby.Naming/TV/SeasonPathParser.cs186
-rw-r--r--Emby.Naming/TV/SeasonPathParserResult.cs18
-rw-r--r--Emby.Naming/Video/CleanDateTimeParser.cs87
-rw-r--r--Emby.Naming/Video/CleanDateTimeResult.cs22
-rw-r--r--Emby.Naming/Video/CleanStringParser.cs49
-rw-r--r--Emby.Naming/Video/CleanStringResult.cs17
-rw-r--r--Emby.Naming/Video/ExtraResolver.cs87
-rw-r--r--Emby.Naming/Video/ExtraResult.cs18
-rw-r--r--Emby.Naming/Video/ExtraRule.cs28
-rw-r--r--Emby.Naming/Video/ExtraRuleType.cs19
-rw-r--r--Emby.Naming/Video/FileStack.cs28
-rw-r--r--Emby.Naming/Video/FlagParser.cs35
-rw-r--r--Emby.Naming/Video/Format3DParser.cs81
-rw-r--r--Emby.Naming/Video/Format3DResult.cs28
-rw-r--r--Emby.Naming/Video/Format3DRule.cs17
-rw-r--r--Emby.Naming/Video/StackResolver.cs216
-rw-r--r--Emby.Naming/Video/StackResult.cs14
-rw-r--r--Emby.Naming/Video/StubResolver.cs44
-rw-r--r--Emby.Naming/Video/StubResult.cs28
-rw-r--r--Emby.Naming/Video/StubTypeRule.cs17
-rw-r--r--Emby.Naming/Video/VideoFileInfo.cs79
-rw-r--r--Emby.Naming/Video/VideoInfo.cs43
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs259
-rw-r--r--Emby.Naming/Video/VideoResolver.cs139
-rw-r--r--Emby.Notifications/Api/NotificationsService.cs (renamed from MediaBrowser.Api/NotificationsService.cs)95
-rw-r--r--Emby.Notifications/CoreNotificationTypes.cs (renamed from Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs)76
-rw-r--r--Emby.Notifications/Emby.Notifications.csproj14
-rw-r--r--Emby.Notifications/NotificationConfigurationFactory.cs (renamed from Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs)4
-rw-r--r--Emby.Notifications/NotificationManager.cs (renamed from Emby.Server.Implementations/Notifications/NotificationManager.cs)120
-rw-r--r--Emby.Notifications/Notifications.cs299
-rw-r--r--Emby.Notifications/Properties/AssemblyInfo.cs (renamed from MediaBrowser.ServerApplication/Properties/AssemblyInfo.cs)24
-rw-r--r--Emby.Photos/Emby.Photos.csproj74
-rw-r--r--Emby.Photos/Emby.Photos.nuget.targets6
-rw-r--r--Emby.Photos/PhotoProvider.cs44
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs215
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs13
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs108
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs9
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs845
-rw-r--r--Emby.Server.Implementations/Archiving/ZipClient.cs40
-rw-r--r--Emby.Server.Implementations/Browser/BrowserLauncher.cs26
-rw-r--r--Emby.Server.Implementations/Channels/ChannelConfigurations.cs29
-rw-r--r--Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs15
-rw-r--r--Emby.Server.Implementations/Channels/ChannelImageProvider.cs10
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs1078
-rw-r--r--Emby.Server.Implementations/Channels/ChannelPostScanTask.cs183
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs14
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs227
-rw-r--r--Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs34
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs14
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs21
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs9
-rw-r--r--Emby.Server.Implementations/Data/ManagedConnection.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs12
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs19
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs1886
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs209
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs141
-rw-r--r--Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs40
-rw-r--r--Emby.Server.Implementations/Devices/CameraUploadsFolder.cs71
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs380
-rw-r--r--Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs451
-rw-r--r--Emby.Server.Implementations/Diagnostics/CommonProcess.cs49
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs542
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj940
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs142
-rw-r--r--Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs71
-rw-r--r--Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs74
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs7
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs75
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs12
-rw-r--r--Emby.Server.Implementations/EntryPoints/SystemEvents.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs49
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageReporter.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs74
-rw-r--r--Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs5
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs2
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs111
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs12
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs21
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs345
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs452
-rw-r--r--Emby.Server.Implementations/HttpServer/IHttpListener.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/LoggerUtils.cs13
-rw-r--r--Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs10
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs9
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs71
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs57
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs15
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs12
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs923
-rw-r--r--Emby.Server.Implementations/HttpServer/StreamWriter.cs8
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs (renamed from Emby.Server.Implementations/ServerManager/WebSocketConnection.cs)57
-rw-r--r--Emby.Server.Implementations/HttpServerFactory.cs76
-rw-r--r--Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs13
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs9
-rw-r--r--Emby.Server.Implementations/IO/IsoManager.cs1
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs76
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs178
-rw-r--r--Emby.Server.Implementations/IO/MemoryStreamProvider.cs29
-rw-r--r--Emby.Server.Implementations/IO/SharpCifsFileSystem.cs3
-rw-r--r--Emby.Server.Implementations/IO/StreamHelper.cs190
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs57
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs88
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs105
-rw-r--r--Emby.Server.Implementations/Library/ExclusiveLiveStream.cs42
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs700
-rw-r--r--Emby.Server.Implementations/Library/LiveStreamHelper.cs181
-rw-r--r--Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs105
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs599
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs (renamed from MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs)53
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs12
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs24
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs33
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs5
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs36
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs27
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs16
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs22
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs152
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs106
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs369
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs110
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs28
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs28
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosValidator.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs85
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs831
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs81
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs27
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs32
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs68
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs315
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs113
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs234
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs1519
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs128
-rw-r--r--Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs82
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs110
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs113
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs125
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs61
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs179
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs79
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs43
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json77
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json100
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr-CA.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json17
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json13
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json23
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json19
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json13
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json15
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs128
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/au.txt8
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/be.txt6
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/de.txt10
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ru.txt5
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/us.txt2
-rw-r--r--Emby.Server.Implementations/Localization/iso6392.txt2
-rw-r--r--Emby.Server.Implementations/Logging/SimpleLogManager.cs83
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs6
-rw-r--r--Emby.Server.Implementations/Migrations/IVersionMigration.cs9
-rw-r--r--Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs11
-rw-r--r--Emby.Server.Implementations/Net/IWebSocket.cs (renamed from MediaBrowser.Controller/Net/IWebSocket.cs)17
-rw-r--r--Emby.Server.Implementations/Net/NetAcceptSocket.cs98
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs35
-rw-r--r--Emby.Server.Implementations/Net/UdpSocket.cs34
-rw-r--r--Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs31
-rw-r--r--Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs168
-rw-r--r--Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs104
-rw-r--r--Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs2170
-rw-r--r--Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs144
-rw-r--r--Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt24
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs166
-rw-r--r--Emby.Server.Implementations/News/NewsEntryPoint.cs5
-rw-r--r--Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs8
-rw-r--r--Emby.Server.Implementations/Notifications/InternalNotificationService.cs61
-rw-r--r--Emby.Server.Implementations/Notifications/Notifications.cs566
-rw-r--r--Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs358
-rw-r--r--Emby.Server.Implementations/Notifications/WebSocketNotifier.cs56
-rw-r--r--Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs12
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs34
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs265
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs31
-rw-r--r--Emby.Server.Implementations/ResourceFileManager.cs74
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs23
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs53
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/SystemEventTrigger.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs35
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs10
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs11
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs314
-rw-r--r--Emby.Server.Implementations/Security/MBLicenseFile.cs12
-rw-r--r--Emby.Server.Implementations/Security/PluginSecurityManager.cs264
-rw-r--r--Emby.Server.Implementations/Serialization/JsonSerializer.cs52
-rw-r--r--Emby.Server.Implementations/ServerApplicationPaths.cs88
-rw-r--r--Emby.Server.Implementations/ServerManager/ServerManager.cs357
-rw-r--r--Emby.Server.Implementations/Services/RequestHelper.cs3
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs48
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs39
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs52
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs90
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs17
-rw-r--r--Emby.Server.Implementations/Services/SwaggerService.cs260
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs2
-rw-r--r--Emby.Server.Implementations/Session/FirebaseSessionController.cs131
-rw-r--r--Emby.Server.Implementations/Session/HttpSessionController.cs148
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs918
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs378
-rw-r--r--Emby.Server.Implementations/Session/WebSocketController.cs208
-rw-r--r--Emby.Server.Implementations/Social/SharingManager.cs101
-rw-r--r--Emby.Server.Implementations/Social/SharingRepository.cs135
-rw-r--r--Emby.Server.Implementations/SystemEvents.cs28
-rw-r--r--Emby.Server.Implementations/TV/SeriesPostScanTask.cs241
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs38
-rw-r--r--Emby.Server.Implementations/TextEncoding/TextEncoding.cs3
-rw-r--r--Emby.Server.Implementations/Threading/CommonTimer.cs1
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs1
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs131
-rw-r--r--Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs143
-rw-r--r--Emby.Server.Implementations/UserViews/DynamicImageProvider.cs48
-rw-r--r--Emby.Server.Implementations/UserViews/FolderImageProvider.cs57
-rw-r--r--Emby.Server.Implementations/packages.config9
-rw-r--r--Emby.Server.Implementations/values.txt0
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs3
-rw-r--r--MediaBrowser.Api/BaseApiService.cs91
-rw-r--r--MediaBrowser.Api/BrandingService.cs8
-rw-r--r--MediaBrowser.Api/ChannelService.cs145
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs38
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs87
-rw-r--r--MediaBrowser.Api/DisplayPreferencesService.cs2
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs40
-rw-r--r--MediaBrowser.Api/FilterService.cs34
-rw-r--r--MediaBrowser.Api/GamesService.cs60
-rw-r--r--MediaBrowser.Api/Images/ImageRequest.cs8
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs154
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs23
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs23
-rw-r--r--MediaBrowser.Api/ItemRefreshService.cs7
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs25
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs683
-rw-r--r--MediaBrowser.Api/Library/LibraryStructureService.cs24
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs291
-rw-r--r--MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs64
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj141
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.nuget.targets6
-rw-r--r--MediaBrowser.Api/Movies/CollectionService.cs24
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs59
-rw-r--r--MediaBrowser.Api/Music/AlbumsService.cs4
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs21
-rw-r--r--MediaBrowser.Api/NewsService.cs2
-rw-r--r--MediaBrowser.Api/PackageService.cs20
-rw-r--r--MediaBrowser.Api/PlaylistService.cs29
-rw-r--r--MediaBrowser.Api/PluginService.cs48
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs2
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs12
-rw-r--r--MediaBrowser.Api/SearchService.cs31
-rw-r--r--MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs18
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs121
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs8
-rw-r--r--MediaBrowser.Api/Social/SharingService.cs178
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs46
-rw-r--r--MediaBrowser.Api/Subtitles/SubtitleService.cs55
-rw-r--r--MediaBrowser.Api/SuggestionsService.cs12
-rw-r--r--MediaBrowser.Api/System/ActivityLogService.cs6
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs12
-rw-r--r--MediaBrowser.Api/System/SystemInfoWebSocketListener.cs49
-rw-r--r--MediaBrowser.Api/System/SystemService.cs15
-rw-r--r--MediaBrowser.Api/TvShowsService.cs67
-rw-r--r--MediaBrowser.Api/UserLibrary/ArtistsService.cs14
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs167
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs62
-rw-r--r--MediaBrowser.Api/UserLibrary/GameGenresService.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs93
-rw-r--r--MediaBrowser.Api/UserLibrary/MusicGenresService.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs60
-rw-r--r--MediaBrowser.Api/UserLibrary/StudiosService.cs13
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs119
-rw-r--r--MediaBrowser.Api/UserLibrary/UserViewsService.cs22
-rw-r--r--MediaBrowser.Api/UserLibrary/YearsService.cs9
-rw-r--r--MediaBrowser.Api/UserService.cs124
-rw-r--r--MediaBrowser.Api/VideosService.cs34
-rw-r--r--MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs18
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs82
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs22
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationManager.cs82
-rw-r--r--MediaBrowser.Common/Events/EventHelper.cs108
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs58
-rw-r--r--MediaBrowser.Common/Extensions/ResourceNotFoundException.cs63
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs165
-rw-r--r--MediaBrowser.Common/IDependencyContainer.cs15
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj90
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.nuget.targets6
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs156
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs76
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs59
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs62
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs294
-rw-r--r--MediaBrowser.Common/Plugins/IDependencyModule.cs7
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs110
-rw-r--r--MediaBrowser.Common/Progress/ActionableProgress.cs74
-rw-r--r--MediaBrowser.Common/Properties/AssemblyInfo.cs27
-rw-r--r--MediaBrowser.Common/Security/IRequiresRegistration.cs15
-rw-r--r--MediaBrowser.Common/Security/ISecurityManager.cs49
-rw-r--r--MediaBrowser.Common/Security/PaymentRequiredException.cs8
-rw-r--r--MediaBrowser.Common/Updates/GithubUpdater.cs278
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs121
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs11
-rw-r--r--MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs9
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs99
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemInfo.cs75
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemResult.cs16
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemType.cs9
-rw-r--r--MediaBrowser.Controller/Channels/ChannelMediaInfo.cs117
-rw-r--r--MediaBrowser.Controller/Channels/ChannelParentalRating.cs15
-rw-r--r--MediaBrowser.Controller/Channels/ChannelSearchInfo.cs14
-rw-r--r--MediaBrowser.Controller/Channels/IChannel.cs76
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs129
-rw-r--r--MediaBrowser.Controller/Channels/IHasCacheKey.cs13
-rw-r--r--MediaBrowser.Controller/Channels/IIndexableChannel.cs16
-rw-r--r--MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs17
-rw-r--r--MediaBrowser.Controller/Channels/ISearchableChannel.cs28
-rw-r--r--MediaBrowser.Controller/Channels/InternalAllChannelMediaQuery.cs30
-rw-r--r--MediaBrowser.Controller/Channels/InternalChannelFeatures.cs61
-rw-r--r--MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs19
-rw-r--r--MediaBrowser.Controller/Chapters/IChapterManager.cs24
-rw-r--r--MediaBrowser.Controller/Collections/CollectionCreationOptions.cs27
-rw-r--r--MediaBrowser.Controller/Collections/CollectionEvents.cs37
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs74
-rw-r--r--MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs46
-rw-r--r--MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs25
-rw-r--r--MediaBrowser.Controller/Connect/ConnectSupporterSummary.cs19
-rw-r--r--MediaBrowser.Controller/Connect/IConnectManager.cs77
-rw-r--r--MediaBrowser.Controller/Connect/UserLinkResult.cs10
-rw-r--r--MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs10
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs96
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceRepository.cs61
-rw-r--r--MediaBrowser.Controller/Dlna/IDlnaManager.cs76
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs49
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs117
-rw-r--r--MediaBrowser.Controller/Drawing/ImageCollageOptions.cs27
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs72
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs108
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs25
-rw-r--r--MediaBrowser.Controller/Drawing/ImageStream.cs29
-rw-r--r--MediaBrowser.Controller/Dto/DtoOptions.cs69
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs86
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs225
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs296
-rw-r--r--MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs31
-rw-r--r--MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs272
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs345
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs158
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs71
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs2600
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs54
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs74
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs387
-rw-r--r--MediaBrowser.Controller/Entities/DayOfWeekHelper.cs71
-rw-r--r--MediaBrowser.Controller/Entities/Extensions.cs46
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs1573
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs130
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs141
-rw-r--r--MediaBrowser.Controller/Entities/GameSystem.cs101
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs153
-rw-r--r--MediaBrowser.Controller/Entities/ICollectionFolder.cs23
-rw-r--r--MediaBrowser.Controller/Entities/IHasAspectRatio.cs14
-rw-r--r--MediaBrowser.Controller/Entities/IHasDisplayOrder.cs15
-rw-r--r--MediaBrowser.Controller/Entities/IHasMediaSources.cs17
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs317
-rw-r--r--MediaBrowser.Controller/Entities/IHasProgramAttributes.cs20
-rw-r--r--MediaBrowser.Controller/Entities/IHasScreenshots.cs10
-rw-r--r--MediaBrowser.Controller/Entities/IHasSeries.cs20
-rw-r--r--MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs13
-rw-r--r--MediaBrowser.Controller/Entities/IHasStartDate.cs9
-rw-r--r--MediaBrowser.Controller/Entities/IHasTrailers.cs38
-rw-r--r--MediaBrowser.Controller/Entities/IHasUserData.cs27
-rw-r--r--MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs12
-rw-r--r--MediaBrowser.Controller/Entities/IItemByName.cs17
-rw-r--r--MediaBrowser.Controller/Entities/IMetadataContainer.cs19
-rw-r--r--MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs12
-rw-r--r--MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs12
-rw-r--r--MediaBrowser.Controller/Entities/IVirtualFolderCreator.cs15
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs243
-rw-r--r--MediaBrowser.Controller/Entities/InternalPeopleQuery.cs21
-rw-r--r--MediaBrowser.Controller/Entities/ItemImageInfo.cs46
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs65
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs216
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs197
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs74
-rw-r--r--MediaBrowser.Controller/Entities/PeopleHelper.cs119
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs229
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs110
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs34
-rw-r--r--MediaBrowser.Controller/Entities/Share.cs15
-rw-r--r--MediaBrowser.Controller/Entities/SourceType.cs10
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs154
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs385
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs272
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs559
-rw-r--r--MediaBrowser.Controller/Entities/TagExtensions.cs35
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs121
-rw-r--r--MediaBrowser.Controller/Entities/User.cs351
-rw-r--r--MediaBrowser.Controller/Entities/UserItemData.cs124
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs156
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs207
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs1778
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs806
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs160
-rw-r--r--MediaBrowser.Controller/Extensions/StringExtensions.cs17
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs122
-rw-r--r--MediaBrowser.Controller/IO/StreamHelper.cs50
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs95
-rw-r--r--MediaBrowser.Controller/IServerApplicationPaths.cs114
-rw-r--r--MediaBrowser.Controller/Library/DeleteOptions.cs8
-rw-r--r--MediaBrowser.Controller/Library/IIntroProvider.cs32
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs559
-rw-r--r--MediaBrowser.Controller/Library/ILibraryMonitor.cs43
-rw-r--r--MediaBrowser.Controller/Library/ILibraryPostScanTask.cs20
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs97
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceProvider.cs32
-rw-r--r--MediaBrowser.Controller/Library/IMetadataFileSaver.cs19
-rw-r--r--MediaBrowser.Controller/Library/IMetadataSaver.cs33
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs25
-rw-r--r--MediaBrowser.Controller/Library/ISearchEngine.cs19
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs65
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs199
-rw-r--r--MediaBrowser.Controller/Library/IUserViewManager.cs22
-rw-r--r--MediaBrowser.Controller/Library/IntroInfo.cs19
-rw-r--r--MediaBrowser.Controller/Library/ItemChangeEventArgs.cs24
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs281
-rw-r--r--MediaBrowser.Controller/Library/ItemUpdateType.cs14
-rw-r--r--MediaBrowser.Controller/Library/LibraryManagerExtensions.cs13
-rw-r--r--MediaBrowser.Controller/Library/MetadataConfigurationStore.cs29
-rw-r--r--MediaBrowser.Controller/Library/NameExtensions.cs26
-rw-r--r--MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs32
-rw-r--r--MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs11
-rw-r--r--MediaBrowser.Controller/Library/Profiler.cs77
-rw-r--r--MediaBrowser.Controller/Library/SearchHintInfo.cs22
-rw-r--r--MediaBrowser.Controller/Library/TVUtils.cs58
-rw-r--r--MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs39
-rw-r--r--MediaBrowser.Controller/LiveTv/ChannelInfo.cs73
-rw-r--r--MediaBrowser.Controller/LiveTv/IListingsProvider.cs19
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs372
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs47
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvService.cs259
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs76
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs165
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs214
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs9
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvException.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs342
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs49
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs73
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs164
-rw-r--r--MediaBrowser.Controller/LiveTv/ProgramInfo.cs207
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingGroup.cs44
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingInfo.cs205
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs12
-rw-r--r--MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs119
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerEventInfo.cs8
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerInfo.cs127
-rw-r--r--MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs10
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj367
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs2236
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs669
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs261
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs17
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs124
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs32
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs20
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs53
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs25
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs66
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs37
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs328
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs9
-rw-r--r--MediaBrowser.Controller/Net/IAuthorizationContext.cs21
-rw-r--r--MediaBrowser.Controller/Net/IHasResultFactory.cs17
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs113
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs49
-rw-r--r--MediaBrowser.Controller/Net/IServerManager.cs65
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs16
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs84
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketListener.cs17
-rw-r--r--MediaBrowser.Controller/Net/LoggedAttribute.cs61
-rw-r--r--MediaBrowser.Controller/Net/SecurityException.cs21
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs40
-rw-r--r--MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs64
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessageInfo.cs16
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationManager.cs40
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationService.cs30
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs14
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationsRepository.cs64
-rw-r--r--MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs17
-rw-r--r--MediaBrowser.Controller/Notifications/UserNotification.cs21
-rw-r--r--MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs49
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs173
-rw-r--r--MediaBrowser.Controller/Persistence/IRepository.cs16
-rw-r--r--MediaBrowser.Controller/Persistence/IUserDataRepository.cs50
-rw-r--r--MediaBrowser.Controller/Persistence/IUserRepository.cs34
-rw-r--r--MediaBrowser.Controller/Persistence/MediaStreamQuery.cs26
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs57
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs227
-rw-r--r--MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs20
-rw-r--r--MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs50
-rw-r--r--MediaBrowser.Controller/Plugins/IServerEntryPoint.cs15
-rw-r--r--MediaBrowser.Controller/Properties/AssemblyInfo.cs27
-rw-r--r--MediaBrowser.Controller/Providers/AlbumInfo.cs28
-rw-r--r--MediaBrowser.Controller/Providers/ArtistInfo.cs14
-rw-r--r--MediaBrowser.Controller/Providers/BookInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/BoxSetInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs119
-rw-r--r--MediaBrowser.Controller/Providers/DynamicImageInfo.cs10
-rw-r--r--MediaBrowser.Controller/Providers/DynamicImageResponse.cs36
-rw-r--r--MediaBrowser.Controller/Providers/EpisodeInfo.cs19
-rw-r--r--MediaBrowser.Controller/Providers/ExtraInfo.cs15
-rw-r--r--MediaBrowser.Controller/Providers/ExtraSource.cs9
-rw-r--r--MediaBrowser.Controller/Providers/GameInfo.cs11
-rw-r--r--MediaBrowser.Controller/Providers/GameSystemInfo.cs11
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs24
-rw-r--r--MediaBrowser.Controller/Providers/IDirectoryService.cs15
-rw-r--r--MediaBrowser.Controller/Providers/IDynamicImageProvider.cs27
-rw-r--r--MediaBrowser.Controller/Providers/IExternalId.cs15
-rw-r--r--MediaBrowser.Controller/Providers/IExtrasProvider.cs20
-rw-r--r--MediaBrowser.Controller/Providers/IForcedProvider.cs10
-rw-r--r--MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs15
-rw-r--r--MediaBrowser.Controller/Providers/IHasLookupInfo.cs8
-rw-r--r--MediaBrowser.Controller/Providers/IHasOrder.cs7
-rw-r--r--MediaBrowser.Controller/Providers/IImageEnhancer.cs61
-rw-r--r--MediaBrowser.Controller/Providers/IImageProvider.cs23
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs10
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageProvider.cs9
-rw-r--r--MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs25
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataProvider.cs21
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataService.cs34
-rw-r--r--MediaBrowser.Controller/Providers/IPreRefreshProvider.cs7
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs178
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteImageProvider.cs39
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs25
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs17
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshMode.cs25
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs32
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs32
-rw-r--r--MediaBrowser.Controller/Providers/ItemLookupInfo.cs47
-rw-r--r--MediaBrowser.Controller/Providers/LiveTvProgramLookupInfo.cs9
-rw-r--r--MediaBrowser.Controller/Providers/LocalImageInfo.cs13
-rw-r--r--MediaBrowser.Controller/Providers/MetadataProviderPriority.cs40
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshMode.cs25
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs73
-rw-r--r--MediaBrowser.Controller/Providers/MetadataResult.cs77
-rw-r--r--MediaBrowser.Controller/Providers/MovieInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/MusicVideoInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/PersonLookupInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/RemoteSearchQuery.cs19
-rw-r--r--MediaBrowser.Controller/Providers/SeasonInfo.cs15
-rw-r--r--MediaBrowser.Controller/Providers/SeriesInfo.cs6
-rw-r--r--MediaBrowser.Controller/Providers/SeriesOrderTypes.cs7
-rw-r--r--MediaBrowser.Controller/Providers/SongInfo.cs16
-rw-r--r--MediaBrowser.Controller/Providers/TrailerInfo.cs7
-rw-r--r--MediaBrowser.Controller/Providers/VideoContentType.cs19
-rw-r--r--MediaBrowser.Controller/Resolvers/BaseItemResolver.cs61
-rw-r--r--MediaBrowser.Controller/Resolvers/IItemResolver.cs48
-rw-r--r--MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs15
-rw-r--r--MediaBrowser.Controller/Resolvers/ResolverPriority.cs27
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfo.cs67
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs48
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationRepository.cs38
-rw-r--r--MediaBrowser.Controller/Security/IEncryptionManager.cs20
-rw-r--r--MediaBrowser.Controller/Session/AuthenticationRequest.cs17
-rw-r--r--MediaBrowser.Controller/Session/ISessionController.cs121
-rw-r--r--MediaBrowser.Controller/Session/ISessionControllerFactory.cs16
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs336
-rw-r--r--MediaBrowser.Controller/Session/SessionEventArgs.cs9
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs309
-rw-r--r--MediaBrowser.Controller/Sorting/IBaseItemComparer.cs17
-rw-r--r--MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs29
-rw-r--r--MediaBrowser.Controller/Sorting/SortExtensions.cs143
-rw-r--r--MediaBrowser.Controller/Sorting/SortHelper.cs25
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleManager.cs79
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs47
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs27
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleResponse.cs12
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs33
-rw-r--r--MediaBrowser.Controller/Sync/IHasDynamicAccess.cs18
-rw-r--r--MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs10
-rw-r--r--MediaBrowser.Controller/Sync/IServerSyncProvider.cs71
-rw-r--r--MediaBrowser.Controller/Sync/ISyncDataProvider.cs58
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs181
-rw-r--r--MediaBrowser.Controller/Sync/ISyncProvider.cs32
-rw-r--r--MediaBrowser.Controller/Sync/SyncedFileInfo.cs34
-rw-r--r--MediaBrowser.Controller/Sync/SyncedItemProgress.cs10
-rw-r--r--MediaBrowser.Controller/TV/ITVSeriesManager.cs20
-rw-r--r--MediaBrowser.LocalMetadata/BaseXmlProvider.cs10
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs24
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj94
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets6
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs62
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs80
-rw-r--r--MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs41
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs241
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs6
-rw-r--r--MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs60
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs18
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs21
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs31
-rw-r--r--MediaBrowser.Model/Activity/ActivityLogEntry.cs68
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs15
-rw-r--r--MediaBrowser.Model/Activity/IActivityRepository.cs12
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs10
-rw-r--r--MediaBrowser.Model/ApiClient/GeneralCommandEventArgs.cs22
-rw-r--r--MediaBrowser.Model/ApiClient/HttpResponseEventArgs.cs33
-rw-r--r--MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs27
-rw-r--r--MediaBrowser.Model/ApiClient/SessionUpdatesEventArgs.cs12
-rw-r--r--MediaBrowser.Model/ApiClient/WakeOnLanInfo.cs13
-rw-r--r--MediaBrowser.Model/Branding/BrandingOptions.cs17
-rw-r--r--MediaBrowser.Model/Channels/AllChannelMediaQuery.cs61
-rw-r--r--MediaBrowser.Model/Channels/ChannelFeatures.cs85
-rw-r--r--MediaBrowser.Model/Channels/ChannelFolderType.cs17
-rw-r--r--MediaBrowser.Model/Channels/ChannelInfo.cs30
-rw-r--r--MediaBrowser.Model/Channels/ChannelItemQuery.cs52
-rw-r--r--MediaBrowser.Model/Channels/ChannelItemSortField.cs13
-rw-r--r--MediaBrowser.Model/Channels/ChannelMediaContentType.cs23
-rw-r--r--MediaBrowser.Model/Channels/ChannelMediaType.cs11
-rw-r--r--MediaBrowser.Model/Channels/ChannelQuery.cs47
-rw-r--r--MediaBrowser.Model/Collections/CollectionCreationResult.cs8
-rw-r--r--MediaBrowser.Model/Configuration/AccessSchedule.cs22
-rw-r--r--MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs57
-rw-r--r--MediaBrowser.Model/Configuration/ChannelOptions.cs8
-rw-r--r--MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs27
-rw-r--r--MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs17
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs36
-rw-r--r--MediaBrowser.Model/Configuration/FanartOptions.cs12
-rw-r--r--MediaBrowser.Model/Configuration/ImageOption.cs29
-rw-r--r--MediaBrowser.Model/Configuration/ImageSavingConvention.cs8
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs51
-rw-r--r--MediaBrowser.Model/Configuration/MetadataConfiguration.cs13
-rw-r--r--MediaBrowser.Model/Configuration/MetadataOptions.cs91
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPlugin.cs17
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginSummary.cs32
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginType.cs15
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs574
-rw-r--r--MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs11
-rw-r--r--MediaBrowser.Model/Configuration/UnratedItem.cs16
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs64
-rw-r--r--MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs23
-rw-r--r--MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs17
-rw-r--r--MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs17
-rw-r--r--MediaBrowser.Model/Connect/ConnectAuthorization.cs20
-rw-r--r--MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs18
-rw-r--r--MediaBrowser.Model/Connect/ConnectPassword.cs19
-rw-r--r--MediaBrowser.Model/Connect/ConnectUser.cs12
-rw-r--r--MediaBrowser.Model/Connect/ConnectUserQuery.cs11
-rw-r--r--MediaBrowser.Model/Connect/ConnectUserServer.cs14
-rw-r--r--MediaBrowser.Model/Connect/PinCreationResult.cs11
-rw-r--r--MediaBrowser.Model/Connect/PinExchangeResult.cs9
-rw-r--r--MediaBrowser.Model/Connect/PinStatusResult.cs10
-rw-r--r--MediaBrowser.Model/Connect/UserLinkType.cs15
-rw-r--r--MediaBrowser.Model/Cryptography/ICryptoProvider.cs13
-rw-r--r--MediaBrowser.Model/Devices/ContentUploadHistory.cs15
-rw-r--r--MediaBrowser.Model/Devices/DeviceInfo.cs67
-rw-r--r--MediaBrowser.Model/Devices/DeviceOptions.cs17
-rw-r--r--MediaBrowser.Model/Devices/DeviceQuery.cs22
-rw-r--r--MediaBrowser.Model/Devices/DevicesOptions.cs15
-rw-r--r--MediaBrowser.Model/Devices/LocalFileInfo.cs11
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcess.cs21
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcessFactory.cs24
-rw-r--r--MediaBrowser.Model/Dlna/AudioOptions.cs86
-rw-r--r--MediaBrowser.Model/Dlna/CodecProfile.cs68
-rw-r--r--MediaBrowser.Model/Dlna/CodecType.cs9
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs284
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs72
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs238
-rw-r--r--MediaBrowser.Model/Dlna/DeviceIdentification.cs61
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs334
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfileInfo.cs24
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfileType.cs8
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs36
-rw-r--r--MediaBrowser.Model/Dlna/DlnaFlags.cs48
-rw-r--r--MediaBrowser.Model/Dlna/DlnaMaps.cs56
-rw-r--r--MediaBrowser.Model/Dlna/DlnaProfileType.cs9
-rw-r--r--MediaBrowser.Model/Dlna/EncodingContext.cs8
-rw-r--r--MediaBrowser.Model/Dlna/HeaderMatchType.cs9
-rw-r--r--MediaBrowser.Model/Dlna/HttpHeaderInfo.cs17
-rw-r--r--MediaBrowser.Model/Dlna/IDeviceDiscovery.cs11
-rw-r--r--MediaBrowser.Model/Dlna/ITranscoderSupport.cs25
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfile.cs113
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs431
-rw-r--r--MediaBrowser.Model/Dlna/PlaybackErrorCode.cs10
-rw-r--r--MediaBrowser.Model/Dlna/ProfileCondition.cs38
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionType.cs11
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs29
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionConfiguration.cs14
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs97
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionOptions.cs8
-rw-r--r--MediaBrowser.Model/Dlna/ResponseProfile.cs49
-rw-r--r--MediaBrowser.Model/Dlna/SearchCriteria.cs51
-rw-r--r--MediaBrowser.Model/Dlna/SearchType.cs12
-rw-r--r--MediaBrowser.Model/Dlna/SortCriteria.cs17
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs1791
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs1056
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfoSorter.cs60
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs22
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs43
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs15
-rw-r--r--MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs8
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs59
-rw-r--r--MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs14
-rw-r--r--MediaBrowser.Model/Dlna/VideoOptions.cs11
-rw-r--r--MediaBrowser.Model/Dlna/XmlAttribute.cs13
-rw-r--r--MediaBrowser.Model/Drawing/DrawingUtils.cs146
-rw-r--r--MediaBrowser.Model/Drawing/ImageFormat.cs30
-rw-r--r--MediaBrowser.Model/Drawing/ImageOrientation.cs15
-rw-r--r--MediaBrowser.Model/Drawing/ImageSize.cs93
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs824
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs55
-rw-r--r--MediaBrowser.Model/Dto/ChapterInfoDto.cs40
-rw-r--r--MediaBrowser.Model/Dto/GameSystemSummary.cs48
-rw-r--r--MediaBrowser.Model/Dto/IHasServerId.cs8
-rw-r--r--MediaBrowser.Model/Dto/IHasSyncInfo.cs14
-rw-r--r--MediaBrowser.Model/Dto/IItemDto.cs15
-rw-r--r--MediaBrowser.Model/Dto/ImageByNameInfo.cs32
-rw-r--r--MediaBrowser.Model/Dto/ImageInfo.cs51
-rw-r--r--MediaBrowser.Model/Dto/ImageOptions.cs110
-rw-r--r--MediaBrowser.Model/Dto/ItemCounts.cs67
-rw-r--r--MediaBrowser.Model/Dto/ItemIndex.cs21
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs228
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceType.cs9
-rw-r--r--MediaBrowser.Model/Dto/MetadataEditorInfo.cs27
-rw-r--r--MediaBrowser.Model/Dto/NameIdPair.cs17
-rw-r--r--MediaBrowser.Model/Dto/NameValuePair.cs28
-rw-r--r--MediaBrowser.Model/Dto/RatingType.cs8
-rw-r--r--MediaBrowser.Model/Dto/RecommendationDto.cs14
-rw-r--r--MediaBrowser.Model/Dto/RecommendationType.cs17
-rw-r--r--MediaBrowser.Model/Dto/SubtitleDownloadOptions.cs29
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs127
-rw-r--r--MediaBrowser.Model/Dto/UserItemDataDto.cs76
-rw-r--r--MediaBrowser.Model/Entities/ChapterInfo.cs29
-rw-r--r--MediaBrowser.Model/Entities/CollectionType.cs63
-rw-r--r--MediaBrowser.Model/Entities/DisplayPreferences.cs123
-rw-r--r--MediaBrowser.Model/Entities/EmptyRequestResult.cs7
-rw-r--r--MediaBrowser.Model/Entities/ExtraType.cs16
-rw-r--r--MediaBrowser.Model/Entities/IHasProviderIds.cs16
-rw-r--r--MediaBrowser.Model/Entities/ImageType.cs58
-rw-r--r--MediaBrowser.Model/Entities/IsoType.cs17
-rw-r--r--MediaBrowser.Model/Entities/ItemReview.cs52
-rw-r--r--MediaBrowser.Model/Entities/LibraryUpdateInfo.cs53
-rw-r--r--MediaBrowser.Model/Entities/LocationType.cs26
-rw-r--r--MediaBrowser.Model/Entities/MBRegistrationRecord.cs14
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs454
-rw-r--r--MediaBrowser.Model/Entities/MediaStreamType.cs25
-rw-r--r--MediaBrowser.Model/Entities/MediaType.cs30
-rw-r--r--MediaBrowser.Model/Entities/MediaUrl.cs9
-rw-r--r--MediaBrowser.Model/Entities/MetadataFields.cs58
-rw-r--r--MediaBrowser.Model/Entities/MetadataProviders.cs41
-rw-r--r--MediaBrowser.Model/Entities/PackageReviewInfo.cs38
-rw-r--r--MediaBrowser.Model/Entities/ParentalRating.cs21
-rw-r--r--MediaBrowser.Model/Entities/PersonType.cs42
-rw-r--r--MediaBrowser.Model/Entities/PluginSecurityInfo.cs21
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs103
-rw-r--r--MediaBrowser.Model/Entities/ScrollDirection.cs17
-rw-r--r--MediaBrowser.Model/Entities/SeriesStatus.cs18
-rw-r--r--MediaBrowser.Model/Entities/SortOrder.cs17
-rw-r--r--MediaBrowser.Model/Entities/TrailerType.cs11
-rw-r--r--MediaBrowser.Model/Entities/UserDataSaveReason.cs34
-rw-r--r--MediaBrowser.Model/Entities/Video3DFormat.cs12
-rw-r--r--MediaBrowser.Model/Entities/VideoType.cs26
-rw-r--r--MediaBrowser.Model/Entities/VirtualFolderInfo.cs54
-rw-r--r--MediaBrowser.Model/Events/GenericEventArgs.cs33
-rw-r--r--MediaBrowser.Model/Extensions/LinqExtensions.cs97
-rw-r--r--MediaBrowser.Model/Extensions/ListHelper.cs41
-rw-r--r--MediaBrowser.Model/Extensions/StringHelper.cs134
-rw-r--r--MediaBrowser.Model/Globalization/CountryInfo.cs33
-rw-r--r--MediaBrowser.Model/Globalization/CultureDto.cs33
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs61
-rw-r--r--MediaBrowser.Model/Globalization/LocalizatonOption.cs8
-rw-r--r--MediaBrowser.Model/IO/FileSystemEntryInfo.cs27
-rw-r--r--MediaBrowser.Model/IO/FileSystemEntryType.cs25
-rw-r--r--MediaBrowser.Model/IO/FileSystemMetadata.cs56
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs450
-rw-r--r--MediaBrowser.Model/IO/IIsoManager.cs34
-rw-r--r--MediaBrowser.Model/IO/IIsoMount.cs22
-rw-r--r--MediaBrowser.Model/IO/IIsoMounter.cs51
-rw-r--r--MediaBrowser.Model/IO/IMemoryStreamFactory.cs12
-rw-r--r--MediaBrowser.Model/IO/IShortcutHandler.cs25
-rw-r--r--MediaBrowser.Model/IO/IZipClient.cs85
-rw-r--r--MediaBrowser.Model/IO/StreamDefaults.cs19
-rw-r--r--MediaBrowser.Model/Library/PlayAccess.cs9
-rw-r--r--MediaBrowser.Model/Library/UserViewQuery.cs32
-rw-r--r--MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs127
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelInfoDto.cs122
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelType.cs19
-rw-r--r--MediaBrowser.Model/LiveTv/DayPattern.cs9
-rw-r--r--MediaBrowser.Model/LiveTv/GuideInfo.cs19
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs103
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvInfo.cs31
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs91
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs58
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs8
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs78
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs10
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramAudio.cs12
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramQuery.cs117
-rw-r--r--MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs72
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingGroupQuery.cs11
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingQuery.cs87
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingStatus.cs14
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs94
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs19
-rw-r--r--MediaBrowser.Model/LiveTv/TimerInfoDto.cs43
-rw-r--r--MediaBrowser.Model/LiveTv/TimerQuery.cs23
-rw-r--r--MediaBrowser.Model/Logging/IConsoleLogger.cs7
-rw-r--r--MediaBrowser.Model/Logging/ILogManager.cs54
-rw-r--r--MediaBrowser.Model/Logging/ILogger.cs78
-rw-r--r--MediaBrowser.Model/Logging/LogHelper.cs97
-rw-r--r--MediaBrowser.Model/Logging/LogSeverity.cs30
-rw-r--r--MediaBrowser.Model/Logging/NullLogger.cs44
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj449
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.nuget.targets6
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs26
-rw-r--r--MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs37
-rw-r--r--MediaBrowser.Model/MediaInfo/Container.cs9
-rw-r--r--MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs16
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs44
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs9
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaInfo.cs68
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaProtocol.cs12
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs45
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs32
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs13
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs11
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs14
-rw-r--r--MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs9
-rw-r--r--MediaBrowser.Model/MediaInfo/VideoCodec.cs14
-rw-r--r--MediaBrowser.Model/Net/EndPointInfo.cs8
-rw-r--r--MediaBrowser.Model/Net/HttpException.cs43
-rw-r--r--MediaBrowser.Model/Net/HttpResponse.cs65
-rw-r--r--MediaBrowser.Model/Net/IAcceptSocket.cs27
-rw-r--r--MediaBrowser.Model/Net/ISocket.cs28
-rw-r--r--MediaBrowser.Model/Net/ISocketFactory.cs51
-rw-r--r--MediaBrowser.Model/Net/IpAddressInfo.cs42
-rw-r--r--MediaBrowser.Model/Net/IpEndPointInfo.cs30
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs359
-rw-r--r--MediaBrowser.Model/Net/NetworkShare.cs31
-rw-r--r--MediaBrowser.Model/Net/NetworkShareType.cs30
-rw-r--r--MediaBrowser.Model/Net/SocketReceiveResult.cs25
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessage.cs22
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessageType.cs22
-rw-r--r--MediaBrowser.Model/Net/WebSocketState.cs38
-rw-r--r--MediaBrowser.Model/News/INewsService.cs17
-rw-r--r--MediaBrowser.Model/News/NewsItem.cs14
-rw-r--r--MediaBrowser.Model/News/NewsQuery.cs9
-rw-r--r--MediaBrowser.Model/Notifications/Notification.cs23
-rw-r--r--MediaBrowser.Model/Notifications/NotificationLevel.cs10
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOption.cs54
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs131
-rw-r--r--MediaBrowser.Model/Notifications/NotificationQuery.cs14
-rw-r--r--MediaBrowser.Model/Notifications/NotificationRequest.cs37
-rw-r--r--MediaBrowser.Model/Notifications/NotificationResult.cs9
-rw-r--r--MediaBrowser.Model/Notifications/NotificationServiceInfo.cs8
-rw-r--r--MediaBrowser.Model/Notifications/NotificationType.cs24
-rw-r--r--MediaBrowser.Model/Notifications/NotificationTypeInfo.cs28
-rw-r--r--MediaBrowser.Model/Notifications/NotificationsSummary.cs9
-rw-r--r--MediaBrowser.Model/Notifications/SendToUserType.cs9
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs20
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationResult.cs8
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistItemQuery.cs37
-rw-r--r--MediaBrowser.Model/Plugins/BasePluginConfiguration.cs10
-rw-r--r--MediaBrowser.Model/Plugins/IHasWebPages.cs9
-rw-r--r--MediaBrowser.Model/Plugins/PluginInfo.cs57
-rw-r--r--MediaBrowser.Model/Plugins/PluginPageInfo.cs13
-rw-r--r--MediaBrowser.Model/Properties/AssemblyInfo.cs23
-rw-r--r--MediaBrowser.Model/Providers/ExternalIdInfo.cs24
-rw-r--r--MediaBrowser.Model/Providers/ExternalUrl.cs17
-rw-r--r--MediaBrowser.Model/Providers/ImageProviderInfo.cs24
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageInfo.cs71
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageQuery.cs15
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageResult.cs28
-rw-r--r--MediaBrowser.Model/Providers/RemoteSearchResult.cs44
-rw-r--r--MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs19
-rw-r--r--MediaBrowser.Model/Providers/SubtitleOptions.cs25
-rw-r--r--MediaBrowser.Model/Providers/SubtitleProviderInfo.cs8
-rw-r--r--MediaBrowser.Model/Querying/AllThemeMediaResult.cs20
-rw-r--r--MediaBrowser.Model/Querying/ArtistsQuery.cs10
-rw-r--r--MediaBrowser.Model/Querying/EpisodeQuery.cs62
-rw-r--r--MediaBrowser.Model/Querying/ItemCountsQuery.cs21
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs220
-rw-r--r--MediaBrowser.Model/Querying/ItemFilter.cs46
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs81
-rw-r--r--MediaBrowser.Model/Querying/ItemsByNameQuery.cs136
-rw-r--r--MediaBrowser.Model/Querying/LatestItemsQuery.cs76
-rw-r--r--MediaBrowser.Model/Querying/MovieRecommendationQuery.cs39
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs66
-rw-r--r--MediaBrowser.Model/Querying/PersonsQuery.cs23
-rw-r--r--MediaBrowser.Model/Querying/QueryFilters.cs31
-rw-r--r--MediaBrowser.Model/Querying/QueryResult.cs23
-rw-r--r--MediaBrowser.Model/Querying/SessionQuery.cs14
-rw-r--r--MediaBrowser.Model/Querying/SimilarItemsQuery.cs29
-rw-r--r--MediaBrowser.Model/Querying/ThemeMediaResult.cs16
-rw-r--r--MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs57
-rw-r--r--MediaBrowser.Model/Querying/UserQuery.cs9
-rw-r--r--MediaBrowser.Model/Reflection/IAssemblyInfo.cs14
-rw-r--r--MediaBrowser.Model/Registration/RegistrationInfo.cs28
-rw-r--r--MediaBrowser.Model/Search/SearchHint.cs154
-rw-r--r--MediaBrowser.Model/Search/SearchHintResult.cs21
-rw-r--r--MediaBrowser.Model/Search/SearchQuery.cs64
-rw-r--r--MediaBrowser.Model/Serialization/IJsonSerializer.cs87
-rw-r--r--MediaBrowser.Model/Serialization/IXmlSerializer.cs46
-rw-r--r--MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs12
-rw-r--r--MediaBrowser.Model/Services/ApiMemberAttribute.cs61
-rw-r--r--MediaBrowser.Model/Services/IAsyncStreamWriter.cs11
-rw-r--r--MediaBrowser.Model/Services/IHasHeaders.cs9
-rw-r--r--MediaBrowser.Model/Services/IHasRequestFilter.cs21
-rw-r--r--MediaBrowser.Model/Services/IHttpRequest.cs45
-rw-r--r--MediaBrowser.Model/Services/IHttpResponse.cs24
-rw-r--r--MediaBrowser.Model/Services/IHttpResult.cs41
-rw-r--r--MediaBrowser.Model/Services/IRequest.cs157
-rw-r--r--MediaBrowser.Model/Services/IRequestFilter.cs8
-rw-r--r--MediaBrowser.Model/Services/IRequiresRequestStream.cs12
-rw-r--r--MediaBrowser.Model/Services/IService.cs12
-rw-r--r--MediaBrowser.Model/Services/IStreamWriter.cs9
-rw-r--r--MediaBrowser.Model/Services/QueryParamCollection.cs229
-rw-r--r--MediaBrowser.Model/Services/RouteAttribute.cs148
-rw-r--r--MediaBrowser.Model/Session/BrowseRequest.cs27
-rw-r--r--MediaBrowser.Model/Session/ClientCapabilities.cs33
-rw-r--r--MediaBrowser.Model/Session/GeneralCommand.cs18
-rw-r--r--MediaBrowser.Model/Session/GeneralCommandType.cs45
-rw-r--r--MediaBrowser.Model/Session/MessageCommand.cs12
-rw-r--r--MediaBrowser.Model/Session/PlayCommand.cs29
-rw-r--r--MediaBrowser.Model/Session/PlayMethod.cs9
-rw-r--r--MediaBrowser.Model/Session/PlayRequest.cs42
-rw-r--r--MediaBrowser.Model/Session/PlaybackProgressInfo.cs110
-rw-r--r--MediaBrowser.Model/Session/PlaybackStartInfo.cs10
-rw-r--r--MediaBrowser.Model/Session/PlaybackStopInfo.cs53
-rw-r--r--MediaBrowser.Model/Session/PlayerStateInfo.cs65
-rw-r--r--MediaBrowser.Model/Session/PlaystateCommand.cs43
-rw-r--r--MediaBrowser.Model/Session/PlaystateRequest.cs15
-rw-r--r--MediaBrowser.Model/Session/SessionInfoDto.cs121
-rw-r--r--MediaBrowser.Model/Session/SessionUserInfo.cs19
-rw-r--r--MediaBrowser.Model/Session/TranscodingInfo.cs54
-rw-r--r--MediaBrowser.Model/Session/UserDataChangeInfo.cs23
-rw-r--r--MediaBrowser.Model/Social/ISharingManager.cs27
-rw-r--r--MediaBrowser.Model/Social/ISharingRepository.cs10
-rw-r--r--MediaBrowser.Model/Social/SocialShareInfo.cs16
-rw-r--r--MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs15
-rw-r--r--MediaBrowser.Model/Sync/DeviceFileInfo.cs9
-rw-r--r--MediaBrowser.Model/Sync/ItemFIleInfo.cs33
-rw-r--r--MediaBrowser.Model/Sync/ItemFileType.cs19
-rw-r--r--MediaBrowser.Model/Sync/LocalItem.cs60
-rw-r--r--MediaBrowser.Model/Sync/LocalItemInfo.cs11
-rw-r--r--MediaBrowser.Model/Sync/LocalItemQuery.cs19
-rw-r--r--MediaBrowser.Model/Sync/SyncCategory.cs19
-rw-r--r--MediaBrowser.Model/Sync/SyncDataRequest.cs16
-rw-r--r--MediaBrowser.Model/Sync/SyncDataResponse.cs13
-rw-r--r--MediaBrowser.Model/Sync/SyncDialogOptions.cs36
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs113
-rw-r--r--MediaBrowser.Model/Sync/SyncJobCreationResult.cs15
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItem.cs107
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItemQuery.cs47
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItemStatus.cs13
-rw-r--r--MediaBrowser.Model/Sync/SyncJobQuery.cs44
-rw-r--r--MediaBrowser.Model/Sync/SyncJobRequest.cs74
-rw-r--r--MediaBrowser.Model/Sync/SyncJobStatus.cs14
-rw-r--r--MediaBrowser.Model/Sync/SyncOptions.cs16
-rw-r--r--MediaBrowser.Model/Sync/SyncParameter.cs13
-rw-r--r--MediaBrowser.Model/Sync/SyncProfileOption.cs37
-rw-r--r--MediaBrowser.Model/Sync/SyncQualityOption.cs32
-rw-r--r--MediaBrowser.Model/Sync/SyncTarget.cs17
-rw-r--r--MediaBrowser.Model/Sync/SyncedItem.cs60
-rw-r--r--MediaBrowser.Model/System/Architecture.cs10
-rw-r--r--MediaBrowser.Model/System/IEnvironmentInfo.cs24
-rw-r--r--MediaBrowser.Model/System/IPowerManagement.cs9
-rw-r--r--MediaBrowser.Model/System/ISystemEvents.cs12
-rw-r--r--MediaBrowser.Model/System/LogFile.cs31
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs41
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs161
-rw-r--r--MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs18
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTask.cs47
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs76
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs80
-rw-r--r--MediaBrowser.Model/Tasks/ITaskTrigger.cs35
-rw-r--r--MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs44
-rw-r--r--MediaBrowser.Model/Tasks/SystemEvent.cs14
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs11
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionStatus.cs29
-rw-r--r--MediaBrowser.Model/Tasks/TaskExecutionOptions.cs11
-rw-r--r--MediaBrowser.Model/Tasks/TaskInfo.cs78
-rw-r--r--MediaBrowser.Model/Tasks/TaskResult.cs58
-rw-r--r--MediaBrowser.Model/Tasks/TaskState.cs22
-rw-r--r--MediaBrowser.Model/Tasks/TaskTriggerInfo.cs52
-rw-r--r--MediaBrowser.Model/Text/ITextEncoding.cs14
-rw-r--r--MediaBrowser.Model/Threading/ITimer.cs10
-rw-r--r--MediaBrowser.Model/Threading/ITimerFactory.cs10
-rw-r--r--MediaBrowser.Model/Updates/CheckForUpdateResult.cs30
-rw-r--r--MediaBrowser.Model/Updates/InstallationInfo.cs44
-rw-r--r--MediaBrowser.Model/Updates/PackageInfo.cs176
-rw-r--r--MediaBrowser.Model/Updates/PackageTargetSystem.cs21
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionClass.cs21
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionInfo.cs95
-rw-r--r--MediaBrowser.Model/Users/AuthenticationResult.cs32
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordAction.cs10
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordResult.cs23
-rw-r--r--MediaBrowser.Model/Users/PinRedeemResult.cs17
-rw-r--r--MediaBrowser.Model/Users/UserAction.cs15
-rw-r--r--MediaBrowser.Model/Users/UserActionType.cs8
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs115
-rw-r--r--MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs9
-rw-r--r--MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs41
-rw-r--r--MediaBrowser.Providers/Books/GoogleBooksProvider.cs45
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs67
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs13
-rw-r--r--MediaBrowser.Providers/Chapters/ChapterManager.cs5
-rw-r--r--MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs13
-rw-r--r--MediaBrowser.Providers/ImagesByName/ImageUtils.cs96
-rw-r--r--MediaBrowser.Providers/LiveTv/AudioRecordingService.cs26
-rw-r--r--MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs26
-rw-r--r--MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs9
-rw-r--r--MediaBrowser.Providers/LiveTv/VideoRecordingService.cs26
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs31
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs105
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs446
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs302
-rw-r--r--MediaBrowser.Providers/Manager/ProviderUtils.cs72
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj186
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets6
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs34
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs73
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs114
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs194
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs20
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs39
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs153
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs23
-rw-r--r--MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs19
-rw-r--r--MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs23
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs10
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs21
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbSearch.cs8
-rw-r--r--MediaBrowser.Providers/Movies/MovieExternalIds.cs16
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Movies/TmdbSettings.cs5
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs117
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs40
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs7
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistProvider.cs11
-rw-r--r--MediaBrowser.Providers/Music/AudioDbExternalIds.cs8
-rw-r--r--MediaBrowser.Providers/Music/FanArtAlbumProvider.cs12
-rw-r--r--MediaBrowser.Providers/Music/FanArtArtistProvider.cs17
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs86
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicExternalIds.cs24
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbImageProvider.cs17
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbItemProvider.cs19
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbProvider.cs46
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonProvider.cs12
-rw-r--r--MediaBrowser.Providers/People/TvdbPersonImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs189
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs48
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs107
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs57
-rw-r--r--MediaBrowser.Providers/TV/DummySeasonProvider.cs37
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs19
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs14
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs16
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs202
-rw-r--r--MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs11
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs42
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs34
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs8
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs10
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs64
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs249
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs16
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs11
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs228
-rw-r--r--MediaBrowser.Providers/TV/TvExternalIds.cs6
-rw-r--r--MediaBrowser.Server.Mono/EmbyServer.csproj69
-rw-r--r--MediaBrowser.Server.Mono/ImageMagickSharp.dll.config5
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj197
-rw-r--r--MediaBrowser.Server.Mono/MonoAppHost.cs44
-rw-r--r--MediaBrowser.Server.Mono/Native/MonoFileSystem.cs4
-rw-r--r--MediaBrowser.Server.Mono/Native/PowerManagement.cs8
-rw-r--r--MediaBrowser.Server.Mono/Program.cs47
-rw-r--r--MediaBrowser.Server.Mono/Properties/launchSettings.json7
-rw-r--r--MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config4
-rw-r--r--MediaBrowser.Server.Mono/SkiaSharp.dll.config5
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs14
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs (renamed from Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs)99
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs (renamed from Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs)26
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs (renamed from Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs)82
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs (renamed from Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs)63
-rw-r--r--MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs (renamed from Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs)37
-rw-r--r--MediaBrowser.Server.Mono/app.config32
-rw-r--r--MediaBrowser.Server.Mono/packages.config10
-rw-r--r--MediaBrowser.ServerApplication/App.config85
-rw-r--r--MediaBrowser.ServerApplication/ApplicationPathHelper.cs51
-rw-r--r--MediaBrowser.ServerApplication/BackgroundService.cs45
-rw-r--r--MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs65
-rw-r--r--MediaBrowser.ServerApplication/Icon.icobin26694 -> 0 bytes
-rw-r--r--MediaBrowser.ServerApplication/ImageEncoderHelper.cs36
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs800
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj279
-rw-r--r--MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs332
-rw-r--r--MediaBrowser.ServerApplication/Native/LoopUtil.cs293
-rw-r--r--MediaBrowser.ServerApplication/Native/PowerManagement.cs17
-rw-r--r--MediaBrowser.ServerApplication/Native/RegisterServer.bat34
-rw-r--r--MediaBrowser.ServerApplication/Native/ServerAuthorization.cs54
-rw-r--r--MediaBrowser.ServerApplication/Native/Standby.cs48
-rw-r--r--MediaBrowser.ServerApplication/Networking/NativeMethods.cs226
-rw-r--r--MediaBrowser.ServerApplication/Networking/NetworkManager.cs175
-rw-r--r--MediaBrowser.ServerApplication/Networking/NetworkShares.cs643
-rw-r--r--MediaBrowser.ServerApplication/Properties/Resources.Designer.cs63
-rw-r--r--MediaBrowser.ServerApplication/Properties/Resources.resx120
-rw-r--r--MediaBrowser.ServerApplication/Resources/Images/mb3logo800.pngbin3983 -> 0 bytes
-rw-r--r--MediaBrowser.ServerApplication/ServerNotifyIcon.cs223
-rw-r--r--MediaBrowser.ServerApplication/Splash/SplashForm.Designer.cs188
-rw-r--r--MediaBrowser.ServerApplication/Splash/SplashForm.cs45
-rw-r--r--MediaBrowser.ServerApplication/Splash/SplashForm.resx666
-rw-r--r--MediaBrowser.ServerApplication/SplashLogo2.pngbin5479 -> 0 bytes
-rw-r--r--MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs65
-rw-r--r--MediaBrowser.ServerApplication/WindowsAppHost.cs124
-rw-r--r--MediaBrowser.ServerApplication/app.manifest51
-rw-r--r--MediaBrowser.ServerApplication/packages.config9
-rw-r--r--MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs4
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs36
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs141
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj86
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets6
-rw-r--r--MediaBrowser.WebDashboard/ServerEntryPoint.cs3
-rw-r--r--MediaBrowser.WebDashboard/packages.config3
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs41
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj97
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets6
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs49
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs70
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs4
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs12
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs12
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs124
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs30
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs12
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/packages.config3
-rw-r--r--MediaBrowser.sln566
-rw-r--r--Mono.Nat/AsyncResults/AsyncResult.cs71
-rw-r--r--Mono.Nat/Enums/MapState.cs36
-rw-r--r--Mono.Nat/Exceptions/MappingException.cs68
-rw-r--r--Mono.Nat/ISearcher.cs6
-rw-r--r--Mono.Nat/Mono.Nat.csproj106
-rw-r--r--Mono.Nat/NatManager.cs86
-rw-r--r--Mono.Nat/NatUtility.cs233
-rw-r--r--Mono.Nat/Pmp/AsyncResults/PortMapAsyncResult.cs52
-rw-r--r--Mono.Nat/Pmp/PmpNatDevice.cs22
-rw-r--r--Mono.Nat/Pmp/PmpSearcher.cs (renamed from Mono.Nat/Pmp/Searchers/PmpSearcher.cs)49
-rw-r--r--Mono.Nat/Upnp/Messages/DiscoverDeviceMessage.cs60
-rw-r--r--Mono.Nat/Upnp/Messages/ErrorMessage.cs63
-rw-r--r--Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs48
-rw-r--r--Mono.Nat/Upnp/Messages/UpnpMessage.cs6
-rw-r--r--Mono.Nat/Upnp/Searchers/UpnpSearcher.cs9
-rw-r--r--Mono.Nat/Upnp/UpnpNatDevice.cs48
-rw-r--r--OpenSubtitlesHandler/OpenSubtitlesHandler.csproj127
-rw-r--r--OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets6
-rw-r--r--RSSDP/CustomHttpHeaders.cs294
-rw-r--r--RSSDP/DiscoveredSsdpDevice.cs74
-rw-r--r--RSSDP/DisposableManagedObjectBase.cs57
-rw-r--r--RSSDP/GlobalSuppressions.csbin7686 -> 0 bytes
-rw-r--r--RSSDP/HttpParserBase.cs438
-rw-r--r--RSSDP/HttpRequestParser.cs2
-rw-r--r--RSSDP/HttpResponseParser.cs4
-rw-r--r--RSSDP/ISsdpCommunicationsServer.cs6
-rw-r--r--RSSDP/IUPnPDeviceValidator.cs27
-rw-r--r--RSSDP/RSSDP.csproj107
-rw-r--r--RSSDP/SsdpCommunicationsServer.cs112
-rw-r--r--RSSDP/SsdpDevice.cs451
-rw-r--r--RSSDP/SsdpDeviceExtensions.cs36
-rw-r--r--RSSDP/SsdpDeviceIcon.cs50
-rw-r--r--RSSDP/SsdpDeviceLocator.cs645
-rw-r--r--RSSDP/SsdpDeviceLocatorBase.cs628
-rw-r--r--RSSDP/SsdpDeviceProperties.cs205
-rw-r--r--RSSDP/SsdpDeviceProperty.cs35
-rw-r--r--RSSDP/SsdpDevicePublisher.cs580
-rw-r--r--RSSDP/SsdpDevicePublisherBase.cs709
-rw-r--r--RSSDP/SsdpEmbeddedDevice.cs11
-rw-r--r--RSSDP/SsdpHelper.cs88
-rw-r--r--RSSDP/SsdpRootDevice.cs96
-rw-r--r--RSSDP/UPnP10DeviceValidator.cs194
-rw-r--r--SharedVersion.cs2
-rw-r--r--SocketHttpListener/Ext.cs128
-rw-r--r--SocketHttpListener/HttpResponse.cs22
-rw-r--r--SocketHttpListener/Net/AuthenticationTypes.cs13
-rw-r--r--SocketHttpListener/Net/EndPointListener.cs433
-rw-r--r--SocketHttpListener/Net/EndPointManager.cs167
-rw-r--r--SocketHttpListener/Net/HttpConnection.cs94
-rw-r--r--SocketHttpListener/Net/HttpEndPointListener.cs539
-rw-r--r--SocketHttpListener/Net/HttpEndPointManager.cs198
-rw-r--r--SocketHttpListener/Net/HttpKnownHeaderNames.cs95
-rw-r--r--SocketHttpListener/Net/HttpListener.cs19
-rw-r--r--SocketHttpListener/Net/HttpListenerContext.Managed.cs100
-rw-r--r--SocketHttpListener/Net/HttpListenerContext.cs136
-rw-r--r--SocketHttpListener/Net/HttpListenerPrefixCollection.cs8
-rw-r--r--SocketHttpListener/Net/HttpListenerRequest.Managed.cs330
-rw-r--r--SocketHttpListener/Net/HttpListenerRequest.cs937
-rw-r--r--SocketHttpListener/Net/HttpListenerRequestUriBuilder.cs445
-rw-r--r--SocketHttpListener/Net/HttpResponseStream.Managed.cs128
-rw-r--r--SocketHttpListener/Net/ListenerPrefix.cs104
-rw-r--r--SocketHttpListener/Net/SocketAcceptor.cs124
-rw-r--r--SocketHttpListener/Net/UriScheme.cs3
-rw-r--r--SocketHttpListener/Net/WebHeaderCollection.cs25
-rw-r--r--SocketHttpListener/Net/WebHeaderEncoding.cs43
-rw-r--r--SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs358
-rw-r--r--SocketHttpListener/Net/WebSockets/HttpWebSocket.Managed.cs84
-rw-r--r--SocketHttpListener/Net/WebSockets/HttpWebSocket.cs160
-rw-r--r--SocketHttpListener/Net/WebSockets/WebSocketCloseStatus.cs31
-rw-r--r--SocketHttpListener/Net/WebSockets/WebSocketContext.cs169
-rw-r--r--SocketHttpListener/Net/WebSockets/WebSocketValidate.cs143
-rw-r--r--SocketHttpListener/Primitives/ITextEncoding.cs5
-rw-r--r--SocketHttpListener/SocketHttpListener.csproj130
-rw-r--r--SocketHttpListener/WebSocket.cs195
-rw-r--r--SocketHttpListener/WebSocketState.cs35
1419 files changed, 27194 insertions, 88596 deletions
diff --git a/.gitignore b/.gitignore
index c74a54d71..14243deb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -233,3 +233,4 @@ pip-log.txt
#Mr Developer
.mr.developer.cfg
MediaBrowser.WebDashboard/dashboard-ui/.idea/
+/.vs
diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj
index 97abe7484..f38fc8101 100644
--- a/BDInfo/BDInfo.csproj
+++ b/BDInfo/BDInfo.csproj
@@ -1,73 +1,12 @@
-<?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>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{88AE38DF-19D7-406F-A6A9-09527719A21E}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>BDInfo</RootNamespace>
- <AssemblyName>BDInfo</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="BDInfoSettings.cs" />
- <Compile Include="BDROM.cs" />
- <Compile Include="BitVector32.cs" />
- <Compile Include="LanguageCodes.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="TSCodecAC3.cs" />
- <Compile Include="TSCodecAVC.cs" />
- <Compile Include="TSCodecDTS.cs" />
- <Compile Include="TSCodecDTSHD.cs" />
- <Compile Include="TSCodecLPCM.cs" />
- <Compile Include="TSCodecMPEG2.cs" />
- <Compile Include="TSCodecMVC.cs" />
- <Compile Include="TSCodecTrueHD.cs" />
- <Compile Include="TSCodecVC1.cs" />
- <Compile Include="TSInterleavedFile.cs" />
- <Compile Include="TSPlaylistFile.cs" />
- <Compile Include="TSStream.cs" />
- <Compile Include="TSStreamBuffer.cs" />
- <Compile Include="TSStreamClip.cs" />
- <Compile Include="TSStreamClipFile.cs" />
- <Compile Include="TSStreamFile.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/BDInfo/BDInfo.nuget.targets b/BDInfo/BDInfo.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/BDInfo/BDInfo.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/BDInfo/BDROM.cs b/BDInfo/BDROM.cs
index d86648364..f00d9825a 100644
--- a/BDInfo/BDROM.cs
+++ b/BDInfo/BDROM.cs
@@ -57,8 +57,6 @@ namespace BDInfo
public Dictionary<string, TSInterleavedFile> InterleavedFiles =
new Dictionary<string, TSInterleavedFile>();
- private static List<string> ExcludeDirs = new List<string> { "ANY!", "AACS", "BDSVM", "ANYVM", "SLYVM" };
-
public delegate bool OnStreamClipFileScanError(
TSStreamClipFile streamClipFile, Exception ex);
@@ -77,7 +75,7 @@ namespace BDInfo
public BDROM(
string path, IFileSystem fileSystem, ITextEncoding textEncoding)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
@@ -336,7 +334,7 @@ namespace BDInfo
private FileSystemMetadata GetDirectoryBDMV(
string path)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
@@ -421,7 +419,7 @@ namespace BDInfo
return dir.Name;
}
- public static int CompareStreamFiles(
+ public int CompareStreamFiles(
TSStreamFile x,
TSStreamFile y)
{
diff --git a/BDInfo/BitVector32.cs b/BDInfo/BitVector32.cs
deleted file mode 100644
index 1ac94bfbe..000000000
--- a/BDInfo/BitVector32.cs
+++ /dev/null
@@ -1,308 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace BDInfo
-{
- using System.Diagnostics;
- using System.Text;
- using System;
-
- /// <devdoc>
- /// <para>Provides a simple light bit vector with easy integer or Boolean access to
- /// a 32 bit storage.</para>
- /// </devdoc>
- public struct BitVector32
- {
- private uint data;
-
- /// <devdoc>
- /// <para>Initializes a new instance of the BitVector32 structure with the specified internal data.</para>
- /// </devdoc>
- public BitVector32(int data)
- {
- this.data = (uint)data;
- }
-
- /// <devdoc>
- /// <para>Initializes a new instance of the BitVector32 structure with the information in the specified
- /// value.</para>
- /// </devdoc>
- public BitVector32(BitVector32 value)
- {
- this.data = value.data;
- }
-
- /// <devdoc>
- /// <para>Gets or sets a value indicating whether all the specified bits are set.</para>
- /// </devdoc>
- public bool this[int bit]
- {
- get
- {
- return (data & bit) == (uint)bit;
- }
- set
- {
- if (value)
- {
- data |= (uint)bit;
- }
- else
- {
- data &= ~(uint)bit;
- }
- }
- }
-
- /// <devdoc>
- /// <para>Gets or sets the value for the specified section.</para>
- /// </devdoc>
- public int this[Section section]
- {
- get
- {
- return (int)((data & (uint)(section.Mask << section.Offset)) >> section.Offset);
- }
- set
- {
- value <<= section.Offset;
- int offsetMask = (0xFFFF & (int)section.Mask) << section.Offset;
- data = (data & ~(uint)offsetMask) | ((uint)value & (uint)offsetMask);
- }
- }
-
- /// <devdoc>
- /// returns the raw data stored in this bit vector...
- /// </devdoc>
- public int Data
- {
- get
- {
- return (int)data;
- }
- }
-
- private static short CountBitsSet(short mask)
- {
-
- // yes, I know there are better algorithms, however, we know the
- // bits are always right aligned, with no holes (i.e. always 00000111,
- // never 000100011), so this is just fine...
- //
- short value = 0;
- while ((mask & 0x1) != 0)
- {
- value++;
- mask >>= 1;
- }
- return value;
- }
-
- /// <devdoc>
- /// <para> Creates the first mask in a series.</para>
- /// </devdoc>
- public static int CreateMask()
- {
- return CreateMask(0);
- }
-
- /// <devdoc>
- /// Creates the next mask in a series.
- /// </devdoc>
- public static int CreateMask(int previous)
- {
- if (previous == 0)
- {
- return 1;
- }
-
- if (previous == unchecked((int)0x80000000))
- {
- throw new InvalidOperationException("Bit vector full");
- }
-
- return previous << 1;
- }
-
- /// <devdoc>
- /// Given a highValue, creates the mask
- /// </devdoc>
- private static short CreateMaskFromHighValue(short highValue)
- {
- short required = 16;
- while ((highValue & 0x8000) == 0)
- {
- required--;
- highValue <<= 1;
- }
-
- ushort value = 0;
- while (required > 0)
- {
- required--;
- value <<= 1;
- value |= 0x1;
- }
-
- return unchecked((short)value);
- }
-
- /// <devdoc>
- /// <para>Creates the first section in a series, with the specified maximum value.</para>
- /// </devdoc>
- public static Section CreateSection(short maxValue)
- {
- return CreateSectionHelper(maxValue, 0, 0);
- }
-
- /// <devdoc>
- /// <para>Creates the next section in a series, with the specified maximum value.</para>
- /// </devdoc>
- public static Section CreateSection(short maxValue, Section previous)
- {
- return CreateSectionHelper(maxValue, previous.Mask, previous.Offset);
- }
-
- private static Section CreateSectionHelper(short maxValue, short priorMask, short priorOffset)
- {
- if (maxValue < 1)
- {
- throw new ArgumentOutOfRangeException("maxValue");
- }
-#if DEBUG
- int maskCheck = CreateMaskFromHighValue(maxValue);
- int offsetCheck = priorOffset + CountBitsSet(priorMask);
- Debug.Assert(maskCheck <= short.MaxValue && offsetCheck < 32, "Overflow on BitVector32");
-#endif
- short offset = (short)(priorOffset + CountBitsSet(priorMask));
- if (offset >= 32)
- {
- throw new InvalidOperationException("Bit vector full");
- }
- return new Section(CreateMaskFromHighValue(maxValue), offset);
- }
-
- public override bool Equals(object o)
- {
- if (!(o is BitVector32))
- {
- return false;
- }
-
- return data == ((BitVector32)o).data;
- }
-
- public override int GetHashCode()
- {
- return base.GetHashCode();
- }
-
- /// <devdoc>
- /// </devdoc>
- public static string ToString(BitVector32 value)
- {
- StringBuilder sb = new StringBuilder(/*"BitVector32{".Length*/12 + /*32 bits*/32 + /*"}".Length"*/1);
- sb.Append("BitVector32{");
- int locdata = (int)value.data;
- for (int i = 0; i < 32; i++)
- {
- if ((locdata & 0x80000000) != 0)
- {
- sb.Append("1");
- }
- else
- {
- sb.Append("0");
- }
- locdata <<= 1;
- }
- sb.Append("}");
- return sb.ToString();
- }
-
- /// <devdoc>
- /// </devdoc>
- public override string ToString()
- {
- return BitVector32.ToString(this);
- }
-
- /// <devdoc>
- /// <para>
- /// Represents an section of the vector that can contain a integer number.</para>
- /// </devdoc>
- public struct Section
- {
- private readonly short mask;
- private readonly short offset;
-
- internal Section(short mask, short offset)
- {
- this.mask = mask;
- this.offset = offset;
- }
-
- public short Mask
- {
- get
- {
- return mask;
- }
- }
-
- public short Offset
- {
- get
- {
- return offset;
- }
- }
-
- public override bool Equals(object o)
- {
- if (o is Section)
- return Equals((Section)o);
- else
- return false;
- }
-
- public bool Equals(Section obj)
- {
- return obj.mask == mask && obj.offset == offset;
- }
-
- public static bool operator ==(Section a, Section b)
- {
- return a.Equals(b);
- }
-
- public static bool operator !=(Section a, Section b)
- {
- return !(a == b);
- }
-
- public override int GetHashCode()
- {
- return base.GetHashCode();
- }
-
- /// <devdoc>
- /// </devdoc>
- public static string ToString(Section value)
- {
- return "Section{0x" + Convert.ToString(value.Mask, 16) + ", 0x" + Convert.ToString(value.Offset, 16) + "}";
- }
-
- /// <devdoc>
- /// </devdoc>
- public override string ToString()
- {
- return Section.ToString(this);
- }
-
- }
- }
-}
diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs
index 46d66f513..da6fd37cc 100644
--- a/BDInfo/TSPlaylistFile.cs
+++ b/BDInfo/TSPlaylistFile.cs
@@ -954,7 +954,7 @@ namespace BDInfo
}
}
- public static int CompareVideoStreams(
+ public int CompareVideoStreams(
TSVideoStream x,
TSVideoStream y)
{
@@ -995,7 +995,7 @@ namespace BDInfo
}
}
- public static int CompareAudioStreams(
+ public int CompareAudioStreams(
TSAudioStream x,
TSAudioStream y)
{
@@ -1067,7 +1067,7 @@ namespace BDInfo
}
}
- public static int CompareTextStreams(
+ public int CompareTextStreams(
TSTextStream x,
TSTextStream y)
{
@@ -1123,7 +1123,7 @@ namespace BDInfo
}
}
- private static int CompareGraphicsStreams(
+ private int CompareGraphicsStreams(
TSGraphicsStream x,
TSGraphicsStream y)
{
@@ -1189,7 +1189,7 @@ namespace BDInfo
}
}
- private static int GetStreamTypeSortIndex(TSStreamType streamType)
+ private int GetStreamTypeSortIndex(TSStreamType streamType)
{
switch (streamType)
{
diff --git a/BDInfo/TSStreamFile.cs b/BDInfo/TSStreamFile.cs
index 31020cbf4..cfd402434 100644
--- a/BDInfo/TSStreamFile.cs
+++ b/BDInfo/TSStreamFile.cs
@@ -865,7 +865,7 @@ namespace BDInfo
k += streamInfoLength;
}
}
- catch (Exception ex)
+ catch
{
// TODO
//Console.WriteLine(ex.Message);
diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj
index 9ed197c59..f38fc8101 100644
--- a/DvdLib/DvdLib.csproj
+++ b/DvdLib/DvdLib.csproj
@@ -1,67 +1,12 @@
-<?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>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>DvdLib</RootNamespace>
- <AssemblyName>DvdLib</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="BigEndianBinaryReader.cs" />
- <Compile Include="Ifo\AudioAttributes.cs" />
- <Compile Include="Ifo\Cell.cs" />
- <Compile Include="Ifo\CellPlaybackInfo.cs" />
- <Compile Include="Ifo\CellPositionInfo.cs" />
- <Compile Include="Ifo\Chapter.cs" />
- <Compile Include="Ifo\Dvd.cs" />
- <Compile Include="Ifo\DvdTime.cs" />
- <Compile Include="Ifo\PgcCommandTable.cs" />
- <Compile Include="Ifo\Program.cs" />
- <Compile Include="Ifo\ProgramChain.cs" />
- <Compile Include="Ifo\Title.cs" />
- <Compile Include="Ifo\UserOperation.cs" />
- <Compile Include="Ifo\VideoAttributes.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/DvdLib/DvdLib.nuget.targets b/DvdLib/DvdLib.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/DvdLib/DvdLib.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/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs
index 3179f73cd..57cb5d6bd 100644
--- a/DvdLib/Ifo/ProgramChain.cs
+++ b/DvdLib/Ifo/ProgramChain.cs
@@ -15,8 +15,6 @@ namespace DvdLib.Ifo
public class ProgramChain
{
- private ushort _unknown1;
-
private byte _programCount;
public readonly List<Program> Programs;
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs
index 6a0cea4df..663728f36 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/Emby.Dlna/Api/DlnaServerService.cs
@@ -1,15 +1,16 @@
using MediaBrowser.Controller.Dlna;
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
+using MediaBrowser.Common.Extensions;
+using System.Text;
+using MediaBrowser.Controller.Net;
+using System.Linq;
+using Emby.Dlna.Main;
-namespace MediaBrowser.Api.Dlna
+namespace Emby.Dlna.Api
{
[Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")]
[Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")]
@@ -98,30 +99,55 @@ namespace MediaBrowser.Api.Dlna
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
public class GetIcon
{
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UuId { get; set; }
[ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Filename { get; set; }
}
- public class DlnaServerService : BaseApiService
+ public class DlnaServerService : IService, IRequiresRequest
{
private readonly IDlnaManager _dlnaManager;
- private readonly IContentDirectory _contentDirectory;
- private readonly IConnectionManager _connectionManager;
- private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
private const string XMLContentType = "text/xml; charset=UTF-8";
- private readonly IMemoryStreamFactory _memoryStreamProvider;
- public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamFactory memoryStreamProvider)
+ public IRequest Request { get; set; }
+ private IHttpResultFactory _resultFactory;
+
+ private IContentDirectory ContentDirectory
+ {
+ get
+ {
+ return DlnaEntryPoint.Current.ContentDirectory;
+ }
+ }
+
+ private IConnectionManager ConnectionManager
+ {
+ get
+ {
+ return DlnaEntryPoint.Current.ConnectionManager;
+ }
+ }
+
+ private IMediaReceiverRegistrar MediaReceiverRegistrar
+ {
+ get
+ {
+ return DlnaEntryPoint.Current.MediaReceiverRegistrar;
+ }
+ }
+
+ public DlnaServerService(IDlnaManager dlnaManager, IHttpResultFactory httpResultFactory)
{
_dlnaManager = dlnaManager;
- _contentDirectory = contentDirectory;
- _connectionManager = connectionManager;
- _mediaReceiverRegistrar = mediaReceiverRegistrar;
- _memoryStreamProvider = memoryStreamProvider;
+ _resultFactory = httpResultFactory;
+ }
+
+ private string GetHeader(string name)
+ {
+ return Request.Headers[name];
}
public object Get(GetDescriptionXml request)
@@ -130,49 +156,53 @@ namespace MediaBrowser.Api.Dlna
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress);
- return ResultFactory.GetResult(xml, XMLContentType);
+ var cacheLength = TimeSpan.FromDays(1);
+ var cacheKey = Request.RawUrl.GetMD5();
+ var bytes = Encoding.UTF8.GetBytes(xml);
+
+ return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
}
public object Get(GetContentDirectory request)
{
- var xml = _contentDirectory.GetServiceXml(Request.Headers.ToDictionary());
+ var xml = ContentDirectory.GetServiceXml(Request.Headers.ToDictionary());
- return ResultFactory.GetResult(xml, XMLContentType);
+ return _resultFactory.GetResult(Request, xml, XMLContentType);
}
public object Get(GetMediaReceiverRegistrar request)
{
- var xml = _mediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
+ var xml = MediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
- return ResultFactory.GetResult(xml, XMLContentType);
+ return _resultFactory.GetResult(Request, xml, XMLContentType);
}
public object Get(GetConnnectionManager request)
{
- var xml = _connectionManager.GetServiceXml(Request.Headers.ToDictionary());
+ var xml = ConnectionManager.GetServiceXml(Request.Headers.ToDictionary());
- return ResultFactory.GetResult(xml, XMLContentType);
+ return _resultFactory.GetResult(Request, xml, XMLContentType);
}
public object Post(ProcessMediaReceiverRegistrarControlRequest request)
{
- var response = PostAsync(request.RequestStream, _mediaReceiverRegistrar);
+ var response = PostAsync(request.RequestStream, MediaReceiverRegistrar);
- return ResultFactory.GetResult(response.Xml, XMLContentType);
+ return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
public object Post(ProcessContentDirectoryControlRequest request)
{
- var response = PostAsync(request.RequestStream, _contentDirectory);
+ var response = PostAsync(request.RequestStream, ContentDirectory);
- return ResultFactory.GetResult(response.Xml, XMLContentType);
+ return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
public object Post(ProcessConnectionManagerControlRequest request)
{
- var response = PostAsync(request.RequestStream, _connectionManager);
+ var response = PostAsync(request.RequestStream, ConnectionManager);
- return ResultFactory.GetResult(response.Xml, XMLContentType);
+ return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
@@ -188,49 +218,76 @@ namespace MediaBrowser.Api.Dlna
});
}
- public object Get(GetIcon request)
+ protected string GetPathValue(int index)
{
- using (var response = _dlnaManager.GetIcon(request.Filename))
+ var pathInfo = Parse(Request.PathInfo);
+ var first = pathInfo[0];
+
+ // backwards compatibility
+ if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase))
{
- using (var ms = _memoryStreamProvider.CreateNew())
- {
- response.Stream.CopyTo(ms);
+ index++;
+ }
- ms.Position = 0;
- var bytes = ms.ToArray();
- return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower());
- }
+ return pathInfo[index];
+ }
+
+ private List<string> Parse(string pathUri)
+ {
+ var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None);
+
+ var pathInfo = actionParts[actionParts.Length - 1];
+
+ var optionsPos = pathInfo.LastIndexOf('?');
+ if (optionsPos != -1)
+ {
+ pathInfo = pathInfo.Substring(0, optionsPos);
}
+
+ var args = pathInfo.Split('/');
+
+ return args.Skip(1).ToList();
+ }
+
+ public object Get(GetIcon request)
+ {
+ var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower();
+
+ var cacheLength = TimeSpan.FromDays(365);
+ var cacheKey = Request.RawUrl.GetMD5();
+
+ return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult<Stream>(_dlnaManager.GetIcon(request.Filename).Stream));
}
public object Subscribe(ProcessContentDirectoryEventRequest request)
{
- return ProcessEventRequest(_contentDirectory);
+ return ProcessEventRequest(ContentDirectory);
}
public object Subscribe(ProcessConnectionManagerEventRequest request)
{
- return ProcessEventRequest(_connectionManager);
+ return ProcessEventRequest(ConnectionManager);
}
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
- return ProcessEventRequest(_mediaReceiverRegistrar);
+ return ProcessEventRequest(MediaReceiverRegistrar);
}
public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{
- return ProcessEventRequest(_contentDirectory);
+ return ProcessEventRequest(ContentDirectory);
}
public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{
- return ProcessEventRequest(_connectionManager);
+ return ProcessEventRequest(ConnectionManager);
}
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
- return ProcessEventRequest(_mediaReceiverRegistrar);
+ return ProcessEventRequest(MediaReceiverRegistrar);
}
private object ProcessEventRequest(IEventManager eventManager)
@@ -257,7 +314,7 @@ namespace MediaBrowser.Api.Dlna
private object GetSubscriptionResponse(EventSubscriptionResponse response)
{
- return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers);
+ return _resultFactory.GetResult(Request, response.Content, response.ContentType, response.Headers);
}
}
}
diff --git a/MediaBrowser.Api/Dlna/DlnaService.cs b/Emby.Dlna/Api/DlnaService.cs
index 4dd71f446..fec610d23 100644
--- a/MediaBrowser.Api/Dlna/DlnaService.cs
+++ b/Emby.Dlna/Api/DlnaService.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Services;
-namespace MediaBrowser.Api.Dlna
+namespace Emby.Dlna.Api
{
[Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
public class GetProfileInfos : IReturn<DeviceProfileInfo[]>
@@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Dlna
}
[Authenticated(Roles = "Admin")]
- public class DlnaService : BaseApiService
+ public class DlnaService : IService
{
private readonly IDlnaManager _dlnaManager;
@@ -52,23 +52,17 @@ namespace MediaBrowser.Api.Dlna
public object Get(GetProfileInfos request)
{
- var result = _dlnaManager.GetProfileInfos().ToArray();
-
- return ToOptimizedResult(result);
+ return _dlnaManager.GetProfileInfos().ToArray();
}
public object Get(GetProfile request)
{
- var result = _dlnaManager.GetProfile(request.Id);
-
- return ToOptimizedResult(result);
+ return _dlnaManager.GetProfile(request.Id);
}
public object Get(GetDefaultProfile request)
{
- var result = _dlnaManager.GetDefaultProfile();
-
- return ToOptimizedResult(result);
+ return _dlnaManager.GetDefaultProfile();
}
public void Delete(DeleteProfile request)
diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs
index 7e0bc6ae8..571e17d06 100644
--- a/Emby.Dlna/Common/StateVariable.cs
+++ b/Emby.Dlna/Common/StateVariable.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System;
namespace Emby.Dlna.Common
{
@@ -10,7 +11,7 @@ namespace Emby.Dlna.Common
public bool SendsEvents { get; set; }
- public List<string> AllowedValues { get; set; }
+ public string[] AllowedValues { get; set; }
public override string ToString()
{
@@ -19,7 +20,7 @@ namespace Emby.Dlna.Common
public StateVariable()
{
- AllowedValues = new List<string>();
+ AllowedValues = Array.Empty<string>();
}
}
}
diff --git a/MediaBrowser.Model/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs
index 71a24d707..6ab337752 100644
--- a/MediaBrowser.Model/Configuration/DlnaOptions.cs
+++ b/Emby.Dlna/Configuration/DlnaOptions.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Model.Configuration
+namespace Emby.Dlna.Configuration
{
public class DlnaOptions
{
@@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
EnableServer = true;
BlastAliveMessages = true;
ClientDiscoveryIntervalSeconds = 60;
- BlastAliveMessageIntervalSeconds = 30;
+ BlastAliveMessageIntervalSeconds = 1800;
}
}
}
diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
index cec885e4a..f19f0dfb5 100644
--- a/Emby.Dlna/ConfigurationExtension.cs
+++ b/Emby.Dlna/ConfigurationExtension.cs
@@ -1,5 +1,5 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
+using Emby.Dlna.Configuration;
using System.Collections.Generic;
namespace Emby.Dlna
@@ -16,7 +16,7 @@ namespace Emby.Dlna
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
- return new List<ConfigurationStore>
+ return new ConfigurationStore[]
{
new ConfigurationStore
{
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
index 2a415c58e..0666eda22 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
@@ -42,7 +42,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string",
SendsEvents = false,
- AllowedValues = new List<string>
+ AllowedValues = new string[]
{
"OK",
"ContentFormatMismatch",
@@ -65,7 +65,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string",
SendsEvents = false,
- AllowedValues = new List<string>
+ AllowedValues = new string[]
{
"Output",
"Input"
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index fbd709010..0aabe099c 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -26,10 +25,9 @@ namespace Emby.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly ILocalizationManager _localization;
- private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager;
- private readonly Func<IMediaEncoder> _mediaEncoder;
+ private readonly IMediaEncoder _mediaEncoder;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
private readonly ITVSeriesManager _tvSeriesManager;
@@ -40,7 +38,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, ITVSeriesManager tvSeriesManager)
+ IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(logger, httpClient)
{
_dlna = dlna;
@@ -50,7 +48,6 @@ namespace Emby.Dlna.ContentDirectory
_config = config;
_userManager = userManager;
_localization = localization;
- _channelManager = channelManager;
_mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager;
_mediaEncoder = mediaEncoder;
@@ -95,10 +92,9 @@ namespace Emby.Dlna.ContentDirectory
SystemUpdateId,
_config,
_localization,
- _channelManager,
_mediaSourceManager,
_userViewManager,
- _mediaEncoder(),
+ _mediaEncoder,
XmlReaderSettingsFactory,
_tvSeriesManager)
.ProcessControlRequest(request);
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
deleted file mode 100644
index 61501635f..000000000
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using System.Linq;
-using System.Xml.Linq;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Dlna.Server;
-
-namespace Emby.Dlna.ContentDirectory
-{
- public class ContentDirectoryBrowser
- {
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
-
- public ContentDirectoryBrowser(IHttpClient httpClient, ILogger logger)
- {
- _httpClient = httpClient;
- _logger = logger;
- }
-
- private static XNamespace UNamespace = "u";
-
- public async Task<QueryResult<ChannelItemInfo>> Browse(ContentDirectoryBrowseRequest request, CancellationToken cancellationToken)
- {
- var options = new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- UserAgent = "Emby",
- RequestContentType = "text/xml",
- LogErrorResponseBody = true,
- Url = request.ContentDirectoryUrl,
- BufferContent = false
- };
-
- options.RequestHeaders["SOAPACTION"] = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse";
-
- options.RequestContent = GetRequestBody(request);
-
- using (var response = await _httpClient.SendAsync(options, "POST"))
- {
- using (var reader = new StreamReader(response.Content))
- {
- var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
-
- var queryResult = new QueryResult<ChannelItemInfo>();
-
- if (doc.Document == null)
- return queryResult;
-
- var responseElement = doc.Document.Descendants(UNamespace + "BrowseResponse").ToList();
-
- var countElement = responseElement.Select(i => i.Element("TotalMatches")).FirstOrDefault(i => i != null);
- var countValue = countElement == null ? null : countElement.Value;
-
- int count;
- if (!string.IsNullOrWhiteSpace(countValue) && int.TryParse(countValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
- {
- queryResult.TotalRecordCount = count;
-
- var resultElement = responseElement.Select(i => i.Element("Result")).FirstOrDefault(i => i != null);
- var resultString = (string)resultElement;
-
- if (resultElement != null)
- {
- var xElement = XElement.Parse(resultString);
- }
- }
-
- return queryResult;
- }
- }
- }
-
- private string GetRequestBody(ContentDirectoryBrowseRequest request)
- {
- var builder = new StringBuilder();
-
- builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-
- builder.Append("<s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>");
- builder.Append("<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
-
- if (string.IsNullOrWhiteSpace(request.ParentId))
- {
- request.ParentId = "1";
- }
-
- builder.AppendFormat("<ObjectID>{0}</ObjectID>", DescriptionXmlBuilder.Escape(request.ParentId));
- builder.Append("<BrowseFlag>BrowseDirectChildren</BrowseFlag>");
-
- //builder.Append("<BrowseFlag>BrowseMetadata</BrowseFlag>");
-
- builder.Append("<Filter>*</Filter>");
-
- request.StartIndex = request.StartIndex ?? 0;
- builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", DescriptionXmlBuilder.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
-
- request.Limit = request.Limit ?? 20;
- if (request.Limit.HasValue)
- {
- builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", DescriptionXmlBuilder.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
- }
-
- builder.Append("<SortCriteria></SortCriteria>");
-
- builder.Append("</u:Browse>");
- builder.Append("</s:Body></s:Envelope>");
-
- return builder.ToString();
- }
- }
-
- public class ContentDirectoryBrowseRequest
- {
- public int? StartIndex { get; set; }
- public int? Limit { get; set; }
- public string ParentId { get; set; }
- public string ContentDirectoryUrl { get; set; }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
index 7a9bc18ad..facc25214 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
@@ -99,7 +99,7 @@ namespace Emby.Dlna.ContentDirectory
DataType = "string",
SendsEvents = false,
- AllowedValues = new List<string>
+ AllowedValues = new string[]
{
"BrowseMetadata",
"BrowseDirectChildren"
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 5d6ef1adf..91ec54b55 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -31,13 +31,13 @@ using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.LiveTv;
namespace Emby.Dlna.ContentDirectory
{
public class ControlHandler : BaseControlHandler
{
private readonly ILibraryManager _libraryManager;
- private readonly IChannelManager _channelManager;
private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
private readonly User _user;
@@ -56,14 +56,13 @@ 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, ITVSeriesManager tvSeriesManager)
+ public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(config, logger, xmlReaderSettingsFactory)
{
_libraryManager = libraryManager;
_userDataManager = userDataManager;
_user = user;
_systemUpdateId = systemUpdateId;
- _channelManager = channelManager;
_userViewManager = userViewManager;
_tvSeriesManager = tvSeriesManager;
_profile = profile;
@@ -125,7 +124,7 @@ namespace Emby.Dlna.ContentDirectory
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
- _userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed,
+ _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -246,6 +245,8 @@ namespace Emby.Dlna.ContentDirectory
int totalCount;
+ var dlnaOptions = _config.GetDlnaConfiguration();
+
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
//writer.WriteStartDocument();
@@ -274,7 +275,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
- _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, item, user, null, null, deviceId, filter);
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
}
provided++;
@@ -300,7 +301,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
- _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, childItem, user, item, serverItem.StubType, deviceId, filter);
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
}
}
}
@@ -310,7 +311,7 @@ namespace Emby.Dlna.ContentDirectory
var resXML = builder.ToString();
- return new List<KeyValuePair<string, string>>
+ return new []
{
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
@@ -385,6 +386,8 @@ namespace Emby.Dlna.ContentDirectory
provided = childrenResult.Items.Length;
+ var dlnaOptions = _config.GetDlnaConfiguration();
+
foreach (var i in childrenResult.Items)
{
if (i.IsDisplayedAsFolder)
@@ -396,7 +399,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
- _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, i, user, item, serverItem.StubType, deviceId, filter);
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
}
}
@@ -458,7 +461,7 @@ namespace Emby.Dlna.ContentDirectory
{
Limit = limit,
StartIndex = startIndex,
- OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray(),
+ OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray(),
User = user,
Recursive = true,
IsMissing = false,
@@ -478,22 +481,22 @@ namespace Emby.Dlna.ContentDirectory
{
if (item is MusicGenre)
{
- return GetMusicGenreItems(item, null, user, sort, startIndex, limit);
+ return GetMusicGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
}
if (item is MusicArtist)
{
- return GetMusicArtistItems(item, null, user, sort, startIndex, limit);
+ return GetMusicArtistItems(item, Guid.Empty, user, sort, startIndex, limit);
}
if (item is Genre)
{
- return GetGenreItems(item, null, user, sort, startIndex, limit);
+ return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
}
if (!stubType.HasValue || stubType.Value != StubType.Folder)
{
- var collectionFolder = item as ICollectionFolder;
+ var collectionFolder = item as IHasCollectionType;
if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{
return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
@@ -507,21 +510,18 @@ namespace Emby.Dlna.ContentDirectory
return GetTvFolders(item, user, stubType, sort, startIndex, limit);
}
- var userView = item as UserView;
- if (userView != null && string.Equals(CollectionType.Folders, userView.ViewType, StringComparison.OrdinalIgnoreCase))
+ if (collectionFolder != null && string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{
return GetFolders(item, user, stubType, sort, startIndex, limit);
}
+ if (collectionFolder != null && string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
+ }
}
if (stubType.HasValue)
{
- var person = item as Person;
- if (person != null)
- {
- return GetItemsFromPerson(person, user, startIndex, limit);
- }
-
if (stubType.Value != StubType.Folder)
{
return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
@@ -530,13 +530,11 @@ namespace Emby.Dlna.ContentDirectory
var folder = (Folder)item;
- var query = new InternalItemsQuery
+ var query = new InternalItemsQuery(user)
{
Limit = limit,
StartIndex = startIndex,
- User = user,
IsVirtualItem = false,
- PresetViews = new string[] { },
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false,
DtoOptions = GetDtoOptions()
@@ -549,6 +547,22 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult);
}
+ private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
+ {
+ var query = new InternalItemsQuery(user)
+ {
+ StartIndex = startIndex,
+ Limit = limit,
+ };
+ query.IncludeItemTypes = new[] { typeof(LiveTvChannel).Name };
+
+ SetSorting(query, sort, false);
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
@@ -747,7 +761,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
- var folders = user.RootFolder.GetChildren(user, true)
+ var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.OrderBy(i => i.SortName)
.Select(i => new ServerItem(i)
{
@@ -856,10 +870,10 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
- query.OrderBy = new Tuple<string, SortOrder>[]
+ query.OrderBy = new ValueTuple<string, SortOrder>[]
{
- new Tuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
- new Tuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
+ new ValueTuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
+ new ValueTuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
};
query.IsResumable = true;
@@ -1004,7 +1018,7 @@ namespace Emby.Dlna.ContentDirectory
{
var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user)
{
- AncestorIds = new[] { parent.Id.ToString("N") },
+ AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex,
Limit = query.Limit
});
@@ -1022,7 +1036,7 @@ namespace Emby.Dlna.ContentDirectory
{
var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
{
- AncestorIds = new[] { parent.Id.ToString("N") },
+ AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex,
Limit = query.Limit
});
@@ -1040,7 +1054,7 @@ namespace Emby.Dlna.ContentDirectory
{
var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
{
- AncestorIds = new[] { parent.Id.ToString("N") },
+ AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex,
Limit = query.Limit
});
@@ -1058,7 +1072,7 @@ namespace Emby.Dlna.ContentDirectory
{
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{
- AncestorIds = new[] { parent.Id.ToString("N") },
+ AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex,
Limit = query.Limit
});
@@ -1076,7 +1090,7 @@ namespace Emby.Dlna.ContentDirectory
{
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{
- AncestorIds = new[] { parent.Id.ToString("N") },
+ AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex,
Limit = query.Limit,
IsFavorite = true
@@ -1105,77 +1119,77 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new Tuple<string, SortOrder>[] { };
+ query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{
- UserId = user.Id.ToString("N"),
+ UserId = user.Id,
Limit = 50,
IncludeItemTypes = new[] { typeof(Audio).Name },
- ParentId = parent == null ? null : parent.Id.ToString("N"),
+ ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true
- }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
}
private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new Tuple<string, SortOrder>[] { };
+ query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var result = _tvSeriesManager.GetNextUp(new NextUpQuery
{
Limit = query.Limit,
StartIndex = query.StartIndex,
- UserId = query.User.Id.ToString("N")
+ UserId = query.User.Id
- }, new List<BaseItem> { parent }, query.DtoOptions);
+ }, new [] { parent }, query.DtoOptions);
return ToResult(result);
}
private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new Tuple<string, SortOrder>[] { };
+ query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{
- UserId = user.Id.ToString("N"),
+ UserId = user.Id,
Limit = 50,
IncludeItemTypes = new[] { typeof(Episode).Name },
- ParentId = parent == null ? null : parent.Id.ToString("N"),
+ ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false
- }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
}
private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new Tuple<string, SortOrder>[] { };
+ query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{
- UserId = user.Id.ToString("N"),
+ UserId = user.Id,
Limit = 50,
IncludeItemTypes = new[] { typeof(Movie).Name },
- ParentId = parent == null ? null : parent.Id.ToString("N"),
+ ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true
- }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
}
- private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
+ private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
Recursive = true,
ParentId = parentId,
- ArtistIds = new[] { item.Id.ToString("N") },
+ ArtistIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Limit = limit,
StartIndex = startIndex,
@@ -1189,13 +1203,13 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
- private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
+ private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
Recursive = true,
ParentId = parentId,
- GenreIds = new[] { item.Id.ToString("N") },
+ GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
Limit = limit,
StartIndex = startIndex,
@@ -1209,13 +1223,13 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
- private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
+ private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
Recursive = true,
ParentId = parentId,
- GenreIds = new[] { item.Id.ToString("N") },
+ GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Limit = limit,
StartIndex = startIndex,
@@ -1229,15 +1243,15 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
- private QueryResult<ServerItem> ToResult(List<BaseItem> result)
+ private QueryResult<ServerItem> ToResult(BaseItem[] result)
{
var serverItems = result
.Select(i => new ServerItem(i))
- .ToArray(result.Count);
+ .ToArray(result.Length);
return new QueryResult<ServerItem>
{
- TotalRecordCount = result.Count,
+ TotalRecordCount = result.Length,
Items = serverItems
};
}
@@ -1247,7 +1261,7 @@ namespace Emby.Dlna.ContentDirectory
var serverItems = result
.Items
.Select(i => new ServerItem(i))
- .ToArray(result.Items.Length);
+ .ToArray();
return new QueryResult<ServerItem>
{
@@ -1264,28 +1278,7 @@ namespace Emby.Dlna.ContentDirectory
sortOrders.Add(ItemSortBy.SortName);
}
- query.OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray();
- }
-
- private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
- {
- var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
- {
- PersonIds = new[] { person.Id.ToString("N") },
- IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- Limit = limit,
- StartIndex = startIndex,
- DtoOptions = GetDtoOptions()
- });
-
- var serverItems = itemsResult.Items.Select(i => new ServerItem(i)).ToArray(itemsResult.Items.Length);
-
- return new QueryResult<ServerItem>
- {
- TotalRecordCount = itemsResult.TotalRecordCount,
- Items = serverItems
- };
+ query.OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray();
}
private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
@@ -1299,7 +1292,7 @@ namespace Emby.Dlna.ContentDirectory
{
return DidlBuilder.IsIdRoot(id)
- ? new ServerItem(user.RootFolder)
+ ? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id, user);
}
@@ -1343,7 +1336,7 @@ namespace Emby.Dlna.ContentDirectory
Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id);
- return new ServerItem(user.RootFolder);
+ return new ServerItem(_libraryManager.GetUserRootFolder());
}
}
diff --git a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
index 8e3821e7c..8e5c07ce2 100644
--- a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
@@ -7,7 +7,7 @@ namespace Emby.Dlna.ContentDirectory
{
public IEnumerable<ServiceAction> GetActions()
{
- var list = new List<ServiceAction>
+ return new []
{
GetSearchCapabilitiesAction(),
GetSortCapabilitiesAction(),
@@ -18,8 +18,6 @@ namespace Emby.Dlna.ContentDirectory
GetXSetBookmarkAction(),
GetBrowseByLetterAction()
};
-
- return list;
}
private ServiceAction GetGetSystemUpdateIDAction()
diff --git a/MediaBrowser.Controller/Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs
index ff951ec9e..2b8ce844a 100644
--- a/MediaBrowser.Controller/Dlna/ControlRequest.cs
+++ b/Emby.Dlna/ControlRequest.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public class ControlRequest
{
diff --git a/MediaBrowser.Controller/Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs
index 8d19a8109..889a6940c 100644
--- a/MediaBrowser.Controller/Dlna/ControlResponse.cs
+++ b/Emby.Dlna/ControlResponse.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public class ControlResponse
{
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index a6fa187d4..42727bb92 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -21,7 +21,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
+using Emby.Dlna.Configuration;
using MediaBrowser.Model.Globalization;
namespace Emby.Dlna.Didl
@@ -62,6 +62,11 @@ namespace Emby.Dlna.Didl
_user = user;
}
+ public static string NormalizeDlnaMediaUrl(string url)
+ {
+ return url + "&dlnaheaders=true";
+ }
+
public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{
var settings = new XmlWriterSettings
@@ -72,28 +77,29 @@ namespace Emby.Dlna.Didl
ConformanceLevel = ConformanceLevel.Fragment
};
- StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-
- using (XmlWriter writer = XmlWriter.Create(builder, settings))
+ using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{
- //writer.WriteStartDocument();
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
+ {
+ //writer.WriteStartDocument();
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
- //didl.SetAttribute("xmlns:sec", NS_SEC);
+ writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+ writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ //didl.SetAttribute("xmlns:sec", NS_SEC);
- WriteXmlRootAttributes(_profile, writer);
+ WriteXmlRootAttributes(_profile, writer);
- WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
+ WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
- writer.WriteFullEndElement();
- //writer.WriteEndDocument();
- }
+ writer.WriteFullEndElement();
+ //writer.WriteEndDocument();
+ }
- return builder.ToString();
+ return builder.ToString();
+ }
}
public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
@@ -136,9 +142,9 @@ namespace Emby.Dlna.Didl
else
{
var parent = item.DisplayParentId;
- if (parent.HasValue)
+ if (!parent.Equals(Guid.Empty))
{
- writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
+ writer.WriteAttributeString("parentID", GetClientId(parent, null));
}
}
@@ -155,11 +161,11 @@ namespace Emby.Dlna.Didl
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- AddAudioResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
+ AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
- AddVideoResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
+ AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
}
}
@@ -181,6 +187,7 @@ namespace Emby.Dlna.Didl
{
var mime = MimeTypes.GetMimeType(input);
+ // TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
{
mime = "video/mpeg";
@@ -189,7 +196,7 @@ namespace Emby.Dlna.Didl
return mime;
}
- private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null)
+ private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
if (streamInfo == null)
{
@@ -197,8 +204,8 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
{
- ItemId = GetClientId(video),
- MediaSources = sources.ToArray(sources.Count),
+ ItemId = video.Id,
+ MediaSources = sources.ToArray(),
Profile = _profile,
DeviceId = deviceId,
MaxBitrate = _profile.MaxStreamingBitrate
@@ -217,10 +224,10 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks,
+ streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
- streamInfo.TargetFramerate,
+ streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic,
@@ -296,11 +303,11 @@ namespace Emby.Dlna.Didl
return true;
}
- private void AddVideoResource(XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
+ private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
- var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
+ var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource;
@@ -361,7 +368,7 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
- streamInfo.TargetFramerate,
+ streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic,
@@ -494,7 +501,7 @@ namespace Emby.Dlna.Didl
return item.Name;
}
- private void AddAudioResource(DlnaOptions options, XmlWriter writer, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
+ private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@@ -504,14 +511,14 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
{
- ItemId = GetClientId(audio),
+ ItemId = audio.Id,
MediaSources = sources.ToArray(sources.Count),
Profile = _profile,
DeviceId = deviceId
});
}
- var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
+ var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource;
@@ -573,7 +580,7 @@ namespace Emby.Dlna.Didl
targetChannels,
targetAudioBitDepth,
streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks,
+ streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString("protocolInfo", String.Format(
@@ -606,7 +613,7 @@ namespace Emby.Dlna.Didl
{
writer.WriteStartElement(string.Empty, "container", NS_DIDL);
- writer.WriteAttributeString("restricted", "0");
+ writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("searchable", "1");
writer.WriteAttributeString("childCount", childCount.ToString(_usCulture));
@@ -628,13 +635,13 @@ namespace Emby.Dlna.Didl
else
{
var parent = folder.DisplayParentId;
- if (!parent.HasValue)
+ if (parent.Equals(Guid.Empty))
{
writer.WriteAttributeString("parentID", "0");
}
else
{
- writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
+ writer.WriteAttributeString("parentID", GetClientId(parent, null));
}
}
}
@@ -669,7 +676,7 @@ namespace Emby.Dlna.Didl
return;
}
- var userdata = _userDataManager.GetUserData(user.Id, item);
+ var userdata = _userDataManager.GetUserData(user, item);
if (userdata.PlaybackPositionTicks > 0)
{
@@ -713,21 +720,24 @@ namespace Emby.Dlna.Didl
AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
}
- if (filter.Contains("dc:description"))
+ if (!(item is Folder))
{
- var desc = item.Overview;
-
- if (!string.IsNullOrWhiteSpace(desc))
+ if (filter.Contains("dc:description"))
{
- AddValue(writer, "dc", "description", desc, NS_DC);
- }
- }
- if (filter.Contains("upnp:longDescription"))
- {
- if (!string.IsNullOrWhiteSpace(item.Overview))
- {
- AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
+ var desc = item.Overview;
+
+ if (!string.IsNullOrWhiteSpace(desc))
+ {
+ AddValue(writer, "dc", "description", desc, NS_DC);
+ }
}
+ //if (filter.Contains("upnp:longDescription"))
+ //{
+ // if (!string.IsNullOrWhiteSpace(item.Overview))
+ // {
+ // AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
+ // }
+ //}
}
if (!string.IsNullOrEmpty(item.OfficialRating))
@@ -823,37 +833,37 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer)
{
- var types = new[]
- {
- PersonType.Director,
- PersonType.Writer,
- PersonType.Producer,
- PersonType.Composer,
- "Creator"
- };
+ //var types = new[]
+ //{
+ // PersonType.Director,
+ // PersonType.Writer,
+ // PersonType.Producer,
+ // PersonType.Composer,
+ // "Creator"
+ //};
- var people = _libraryManager.GetPeople(item);
+ //var people = _libraryManager.GetPeople(item);
- var index = 0;
+ //var index = 0;
- // Seeing some LG models locking up due content with large lists of people
- // The actual issue might just be due to processing a more metadata than it can handle
- var limit = 6;
+ //// Seeing some LG models locking up due content with large lists of people
+ //// The actual issue might just be due to processing a more metadata than it can handle
+ //var limit = 6;
- foreach (var actor in people)
- {
- var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
- ?? PersonType.Actor;
+ //foreach (var actor in people)
+ //{
+ // var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
+ // ?? PersonType.Actor;
- AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
+ // AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
- index++;
+ // index++;
- if (index >= limit)
- {
- break;
- }
- }
+ // if (index >= limit)
+ // {
+ // break;
+ // }
+ //}
}
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
@@ -935,19 +945,6 @@ namespace Emby.Dlna.Didl
{
ImageDownloadInfo imageInfo = null;
- if (context is UserView)
- {
- var episode = item as Episode;
- if (episode != null)
- {
- var parent = episode.Series;
- if (parent != null)
- {
- imageInfo = GetImageInfo(parent);
- }
- }
- }
-
// Finally, just use the image from the item
if (imageInfo == null)
{
@@ -959,34 +956,7 @@ namespace Emby.Dlna.Didl
return;
}
- var playbackPercentage = 0;
- var unplayedCount = 0;
-
- if (item is Video)
- {
- var userData = _userDataManager.GetUserDataDto(item, _user);
-
- playbackPercentage = Convert.ToInt32(userData.PlayedPercentage ?? 0);
- if (playbackPercentage >= 100 || userData.Played)
- {
- playbackPercentage = 100;
- }
- }
- else if (item is Series || item is Season || item is BoxSet)
- {
- var userData = _userDataManager.GetUserDataDto(item, _user);
-
- if (userData.Played)
- {
- playbackPercentage = 100;
- }
- else
- {
- unplayedCount = userData.UnplayedItemCount ?? 0;
- }
- }
-
- var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, playbackPercentage, unplayedCount, "jpg");
+ var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
@@ -994,7 +964,7 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement();
// TOOD: Remove these default values
- var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, playbackPercentage, unplayedCount, "jpg");
+ var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
if (!_profile.EnableAlbumArtInDidl)
@@ -1009,15 +979,15 @@ namespace Emby.Dlna.Didl
}
}
- AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN");
+ AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
- if (!_profile.EnableSingleAlbumArtLimit)
+ if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{
- AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "jpg", "JPEG_LRG");
- AddImageResElement(item, writer, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED");
- AddImageResElement(item, writer, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM");
- AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG");
- AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN");
+ AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
+ AddImageResElement(item, writer, 1024, 768, "jpg", "JPEG_MED");
+ AddImageResElement(item, writer, 640, 480, "jpg", "JPEG_SM");
+ AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
+ AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
}
}
@@ -1035,8 +1005,6 @@ namespace Emby.Dlna.Didl
XmlWriter writer,
int maxWidth,
int maxHeight,
- int playbackPercentage,
- int unplayedCount,
string format,
string org_Pn)
{
@@ -1047,7 +1015,7 @@ namespace Emby.Dlna.Didl
return;
}
- var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format);
+ var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@@ -1151,7 +1119,7 @@ namespace Emby.Dlna.Didl
return new ImageDownloadInfo
{
- ItemId = item.Id.ToString("N"),
+ ItemId = item.Id,
Type = type,
ImageTag = tag,
Width = width,
@@ -1163,7 +1131,7 @@ namespace Emby.Dlna.Didl
class ImageDownloadInfo
{
- internal string ItemId;
+ internal Guid ItemId;
internal string ImageTag;
internal ImageType Type;
@@ -1202,25 +1170,16 @@ namespace Emby.Dlna.Didl
return id;
}
- public static string GetClientId(IHasMediaSources item)
+ private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
{
- var id = item.Id.ToString("N");
-
- return id;
- }
-
- private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, int playbackPercentage, int unplayedCount, string format)
- {
- var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/{7}/{8}",
+ var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
_serverAddress,
- info.ItemId,
+ info.ItemId.ToString("N"),
info.Type,
info.ImageTag,
format,
maxWidth.ToString(CultureInfo.InvariantCulture),
- maxHeight.ToString(CultureInfo.InvariantCulture),
- playbackPercentage.ToString(CultureInfo.InvariantCulture),
- unplayedCount.ToString(CultureInfo.InvariantCulture)
+ maxHeight.ToString(CultureInfo.InvariantCulture)
);
var width = info.Width;
@@ -1235,7 +1194,7 @@ namespace Emby.Dlna.Didl
Height = height.Value,
Width = width.Value
- }, null, null, maxWidth, maxHeight);
+ }, 0, 0, maxWidth, maxHeight);
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
@@ -1249,6 +1208,9 @@ namespace Emby.Dlna.Didl
}
}
+ // just lie
+ info.IsDirectStream = true;
+
return new ImageUrlInfo
{
Url = url,
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 8caa086ee..62d1eb57c 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -132,55 +132,55 @@ namespace Emby.Dlna
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
{
- if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription))
+ if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
{
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
+ if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
{
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
+ if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
{
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
+ if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
{
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
+ if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
{
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
+ if (!string.IsNullOrEmpty(profileInfo.ModelName))
{
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
+ if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
{
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
+ if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
{
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
return false;
}
- if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
+ if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
{
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
return false;
@@ -220,7 +220,7 @@ namespace Emby.Dlna
}
else
{
- var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(headers.Count));
+ var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
_logger.Debug("No matching device profile found. {0}", headerString);
}
@@ -235,7 +235,7 @@ namespace Emby.Dlna
private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
{
// Handle invalid user setup
- if (string.IsNullOrWhiteSpace(header.Name))
+ if (string.IsNullOrEmpty(header.Name))
{
return false;
}
@@ -332,7 +332,7 @@ namespace Emby.Dlna
public DeviceProfile GetProfile(string id)
{
- if (string.IsNullOrWhiteSpace(id))
+ if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
@@ -429,7 +429,7 @@ namespace Emby.Dlna
{
profile = ReserializeProfile(profile);
- if (string.IsNullOrWhiteSpace(profile.Name))
+ if (string.IsNullOrEmpty(profile.Name))
{
throw new ArgumentException("Profile is missing Name");
}
@@ -444,11 +444,11 @@ namespace Emby.Dlna
{
profile = ReserializeProfile(profile);
- if (string.IsNullOrWhiteSpace(profile.Id))
+ if (string.IsNullOrEmpty(profile.Id))
{
throw new ArgumentException("Profile is missing Id");
}
- if (string.IsNullOrWhiteSpace(profile.Name))
+ if (string.IsNullOrEmpty(profile.Name))
{
throw new ArgumentException("Profile is missing Name");
}
@@ -531,7 +531,7 @@ namespace Emby.Dlna
}
}
- class DlnaProfileEntryPoint : IServerEntryPoint
+ class DlnaProfileEntryPoint /*: IServerEntryPoint*/
{
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
@@ -546,7 +546,7 @@ namespace Emby.Dlna
public void Run()
{
- //DumpProfiles();
+ DumpProfiles();
}
private void DumpProfiles()
@@ -595,7 +595,6 @@ namespace Emby.Dlna
public void Dispose()
{
- GC.SuppressFinalize(this);
}
}
} \ No newline at end of file
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index eb8b178a8..f71d54422 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -1,121 +1,17 @@
-<?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>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Dlna</RootNamespace>
- <AssemblyName>Emby.Dlna</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="Common\Argument.cs" />
- <Compile Include="Common\DeviceIcon.cs" />
- <Compile Include="Common\DeviceService.cs" />
- <Compile Include="Common\ServiceAction.cs" />
- <Compile Include="Common\StateVariable.cs" />
- <Compile Include="ConfigurationExtension.cs" />
- <Compile Include="ConnectionManager\ConnectionManager.cs" />
- <Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
- <Compile Include="ConnectionManager\ControlHandler.cs" />
- <Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
- <Compile Include="ContentDirectory\ContentDirectory.cs" />
- <Compile Include="ContentDirectory\ContentDirectoryBrowser.cs" />
- <Compile Include="ContentDirectory\ContentDirectoryXmlBuilder.cs" />
- <Compile Include="ContentDirectory\ControlHandler.cs" />
- <Compile Include="ContentDirectory\ServiceActionListBuilder.cs" />
- <Compile Include="Didl\DidlBuilder.cs" />
- <Compile Include="Didl\Filter.cs" />
- <Compile Include="Didl\StringWriterWithEncoding.cs" />
- <Compile Include="DlnaManager.cs" />
- <Compile Include="Eventing\EventManager.cs" />
- <Compile Include="Eventing\EventSubscription.cs" />
- <Compile Include="Main\DlnaEntryPoint.cs" />
- <Compile Include="MediaReceiverRegistrar\ControlHandler.cs" />
- <Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrar.cs" />
- <Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrarXmlBuilder.cs" />
- <Compile Include="MediaReceiverRegistrar\ServiceActionListBuilder.cs" />
- <Compile Include="PlayTo\CurrentIdEventArgs.cs" />
- <Compile Include="PlayTo\Device.cs" />
- <Compile Include="PlayTo\DeviceInfo.cs" />
- <Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
- <Compile Include="PlayTo\PlaybackStartEventArgs.cs" />
- <Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
- <Compile Include="PlayTo\PlaylistItem.cs" />
- <Compile Include="PlayTo\PlaylistItemFactory.cs" />
- <Compile Include="PlayTo\PlayToController.cs" />
- <Compile Include="PlayTo\PlayToManager.cs" />
- <Compile Include="PlayTo\SsdpHttpClient.cs" />
- <Compile Include="PlayTo\TransportCommands.cs" />
- <Compile Include="PlayTo\TRANSPORTSTATE.cs" />
- <Compile Include="PlayTo\TransportStateEventArgs.cs" />
- <Compile Include="PlayTo\uBaseObject.cs" />
- <Compile Include="PlayTo\uParser.cs" />
- <Compile Include="PlayTo\uParserObject.cs" />
- <Compile Include="PlayTo\UpnpContainer.cs" />
- <Compile Include="PlayTo\uPnpNamespaces.cs" />
- <Compile Include="Profiles\DefaultProfile.cs" />
- <Compile Include="Profiles\DenonAvrProfile.cs" />
- <Compile Include="Profiles\DirectTvProfile.cs" />
- <Compile Include="Profiles\DishHopperJoeyProfile.cs" />
- <Compile Include="Profiles\Foobar2000Profile.cs" />
- <Compile Include="Profiles\LgTvProfile.cs" />
- <Compile Include="Profiles\LinksysDMA2100Profile.cs" />
- <Compile Include="Profiles\MarantzProfile.cs" />
- <Compile Include="Profiles\MediaMonkeyProfile.cs" />
- <Compile Include="Profiles\PanasonicVieraProfile.cs" />
- <Compile Include="Profiles\PopcornHourProfile.cs" />
- <Compile Include="Profiles\SamsungSmartTvProfile.cs" />
- <Compile Include="Profiles\SharpSmartTvProfile.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2013.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2014.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2015.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2016.cs" />
- <Compile Include="Profiles\SonyBlurayPlayerProfile.cs" />
- <Compile Include="Profiles\SonyBravia2010Profile.cs" />
- <Compile Include="Profiles\SonyBravia2011Profile.cs" />
- <Compile Include="Profiles\SonyBravia2012Profile.cs" />
- <Compile Include="Profiles\SonyBravia2013Profile.cs" />
- <Compile Include="Profiles\SonyBravia2014Profile.cs" />
- <Compile Include="Profiles\SonyPs3Profile.cs" />
- <Compile Include="Profiles\SonyPs4Profile.cs" />
- <Compile Include="Profiles\WdtvLiveProfile.cs" />
- <Compile Include="Profiles\XboxOneProfile.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Server\DescriptionXmlBuilder.cs" />
- <Compile Include="Server\UpnpDevice.cs" />
- <Compile Include="Service\BaseControlHandler.cs" />
- <Compile Include="Service\BaseService.cs" />
- <Compile Include="Service\ControlErrorHandler.cs" />
- <Compile Include="Service\ServiceXmlBuilder.cs" />
- <Compile Include="Ssdp\DeviceDiscovery.cs" />
- <Compile Include="Ssdp\Extensions.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\RSSDP\RSSDP.csproj" />
</ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
<ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" />
<EmbeddedResource Include="Images\logo120.png" />
@@ -128,27 +24,7 @@
<EmbeddedResource Include="Images\people480.jpg" />
<EmbeddedResource Include="Images\people480.png" />
</ItemGroup>
- <ItemGroup>
- <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="..\RSSDP\RSSDP.csproj">
- <Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
- <Name>RSSDP</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
- </ItemGroup>
+
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Default.xml" />
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
@@ -179,12 +55,5 @@
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Marantz.xml" />
</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
+
+</Project>
diff --git a/MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs
index 8b551c2a7..1c405e601 100644
--- a/MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs
+++ b/Emby.Dlna/EventSubscriptionResponse.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public class EventSubscriptionResponse
{
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs
index 67eac640e..0638cff89 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/EventManager.cs
@@ -85,7 +85,7 @@ namespace Emby.Dlna.Eventing
int val;
- if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
+ if (int.TryParse(header, NumberStyles.Integer, _usCulture, out val))
{
return val;
}
@@ -118,7 +118,7 @@ namespace Emby.Dlna.Eventing
};
response.Headers["SID"] = subscriptionId;
- response.Headers["TIMEOUT"] = string.IsNullOrWhiteSpace(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
+ response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
return response;
}
diff --git a/MediaBrowser.Controller/Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs
index 38d96e607..5f889a34c 100644
--- a/MediaBrowser.Controller/Dlna/IConnectionManager.cs
+++ b/Emby.Dlna/IConnectionManager.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public interface IConnectionManager : IEventManager, IUpnpService
{
diff --git a/MediaBrowser.Controller/Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs
index 28635d9b7..d63af4ef2 100644
--- a/MediaBrowser.Controller/Dlna/IContentDirectory.cs
+++ b/Emby.Dlna/IContentDirectory.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public interface IContentDirectory : IEventManager, IUpnpService
{
diff --git a/MediaBrowser.Controller/Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs
index 3af357a17..e90476fe3 100644
--- a/MediaBrowser.Controller/Dlna/IEventManager.cs
+++ b/Emby.Dlna/IEventManager.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public interface IEventManager
{
diff --git a/MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs
index 6b76783a5..cb43221e5 100644
--- a/MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs
+++ b/Emby.Dlna/IMediaReceiverRegistrar.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
{
diff --git a/MediaBrowser.Controller/Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs
index 3511740ad..caae87ba3 100644
--- a/MediaBrowser.Controller/Dlna/IUpnpService.cs
+++ b/Emby.Dlna/IUpnpService.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Controller.Dlna
+namespace Emby.Dlna
{
public interface IUpnpService
{
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 5eeab3dec..4bab30337 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -8,13 +8,12 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.TV;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
using MediaBrowser.Model.Logging;
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Net;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
@@ -22,13 +21,14 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Xml;
using Rssdp;
using Rssdp.Infrastructure;
using System.Threading;
namespace Emby.Dlna.Main
{
- public class DlnaEntryPoint : IServerEntryPoint
+ public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
@@ -48,8 +48,6 @@ namespace Emby.Dlna.Main
private readonly IDeviceDiscovery _deviceDiscovery;
- private bool _ssdpHandlerStarted;
- private bool _dlnaServerStarted;
private SsdpDevicePublisher _Publisher;
private readonly ITimerFactory _timerFactory;
@@ -59,6 +57,12 @@ namespace Emby.Dlna.Main
private ISsdpCommunicationsServer _communicationsServer;
+ internal IContentDirectory ContentDirectory { get; private set; }
+ internal IConnectionManager ConnectionManager { get; private set; }
+ internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
+
+ public static DlnaEntryPoint Current;
+
public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager,
IServerApplicationHost appHost,
@@ -69,9 +73,17 @@ namespace Emby.Dlna.Main
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
- ILocalizationManager localization,
+ ILocalizationManager localizationManager,
IMediaSourceManager mediaSourceManager,
- IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, INetworkManager networkManager)
+ IDeviceDiscovery deviceDiscovery,
+ IMediaEncoder mediaEncoder,
+ ISocketFactory socketFactory,
+ ITimerFactory timerFactory,
+ IEnvironmentInfo environmentInfo,
+ INetworkManager networkManager,
+ IUserViewManager userViewManager,
+ IXmlReaderSettingsFactory xmlReaderSettingsFactory,
+ ITVSeriesManager tvSeriesManager)
{
_config = config;
_appHost = appHost;
@@ -82,7 +94,7 @@ namespace Emby.Dlna.Main
_dlnaManager = dlnaManager;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
- _localization = localization;
+ _localization = localizationManager;
_mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder;
@@ -91,6 +103,26 @@ namespace Emby.Dlna.Main
_environmentInfo = environmentInfo;
_networkManager = networkManager;
_logger = logManager.GetLogger("Dlna");
+
+ ContentDirectory = new ContentDirectory.ContentDirectory(dlnaManager,
+ userDataManager,
+ imageProcessor,
+ libraryManager,
+ config,
+ userManager,
+ _logger,
+ httpClient,
+ localizationManager,
+ mediaSourceManager,
+ userViewManager,
+ mediaEncoder,
+ xmlReaderSettingsFactory,
+ tvSeriesManager);
+
+ ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient, xmlReaderSettingsFactory);
+
+ MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config, xmlReaderSettingsFactory);
+ Current = this;
}
public void Run()
@@ -99,20 +131,9 @@ namespace Emby.Dlna.Main
ReloadComponents();
- _config.ConfigurationUpdated += _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
}
- private bool _lastEnableUpnP;
- void _config_ConfigurationUpdated(object sender, EventArgs e)
- {
- if (_lastEnableUpnP != _config.Configuration.EnableUPnP)
- {
- ReloadComponents();
- }
- _lastEnableUpnP = _config.Configuration.EnableUPnP;
- }
-
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
@@ -125,29 +146,22 @@ namespace Emby.Dlna.Main
{
var options = _config.GetDlnaConfiguration();
- if (!_ssdpHandlerStarted)
- {
- StartSsdpHandler();
- }
-
- var isServerStarted = _dlnaServerStarted;
+ StartSsdpHandler();
- if (options.EnableServer && !isServerStarted)
+ if (options.EnableServer)
{
- await StartDlnaServer().ConfigureAwait(false);
+ await StartDevicePublisher(options).ConfigureAwait(false);
}
- else if (!options.EnableServer && isServerStarted)
+ else
{
- DisposeDlnaServer();
+ DisposeDevicePublisher();
}
- var isPlayToStarted = _manager != null;
-
- if (options.EnablePlayTo && !isPlayToStarted)
+ if (options.EnablePlayTo)
{
StartPlayToManager();
}
- else if (!options.EnablePlayTo && isPlayToStarted)
+ else
{
DisposePlayToManager();
}
@@ -165,12 +179,9 @@ namespace Emby.Dlna.Main
{
IsShared = true
};
- }
-
- StartPublishing(_communicationsServer);
- _ssdpHandlerStarted = true;
- StartDeviceDiscovery(_communicationsServer);
+ StartDeviceDiscovery(_communicationsServer);
+ }
}
catch (Exception ex)
{
@@ -183,12 +194,6 @@ namespace Emby.Dlna.Main
_logger.Debug(msg);
}
- private void StartPublishing(ISsdpCommunicationsServer communicationsServer)
- {
- SsdpDevicePublisherBase.LogFunction = LogMessage;
- _Publisher = new SsdpDevicePublisher(communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
- }
-
private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer)
{
try
@@ -205,6 +210,7 @@ namespace Emby.Dlna.Main
{
try
{
+ _logger.Info("Disposing DeviceDiscovery");
((DeviceDiscovery)_deviceDiscovery).Dispose();
}
catch (Exception ex)
@@ -213,29 +219,27 @@ namespace Emby.Dlna.Main
}
}
- private void DisposeSsdpHandler()
+ public async Task StartDevicePublisher(Configuration.DlnaOptions options)
{
- DisposeDeviceDiscovery();
-
- try
+ if (!options.BlastAliveMessages)
{
- ((DeviceDiscovery)_deviceDiscovery).Dispose();
-
- _ssdpHandlerStarted = false;
+ return;
}
- catch (Exception ex)
+
+ if (_Publisher != null)
{
- _logger.ErrorException("Error stopping ssdp handlers", ex);
+ return;
}
- }
- public async Task StartDlnaServer()
- {
try
{
+ _Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
+ _Publisher.LogFunction = LogMessage;
+ _Publisher.SupportPnpRootDevice = false;
+
await RegisterServerEndpoints().ConfigureAwait(false);
- _dlnaServerStarted = true;
+ _Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
}
catch (Exception ex)
{
@@ -245,23 +249,15 @@ namespace Emby.Dlna.Main
private async Task RegisterServerEndpoints()
{
- if (!_config.GetDlnaConfiguration().BlastAliveMessages)
- {
- return;
- }
-
- var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds;
- _Publisher.SupportPnpRootDevice = false;
-
var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList();
var udn = CreateUuid(_appHost.SystemId);
foreach (var address in addresses)
{
- //if (IPAddress.IsLoopback(address))
+ // TODO: Remove this condition on platforms that support it
+ //if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
//{
- // // Should we allow this?
// continue;
//}
@@ -274,7 +270,7 @@ namespace Emby.Dlna.Main
var device = new SsdpRootDevice
{
- CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info.
+ CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
Location = uri, // Must point to the URL that serves your devices UPnP description document.
FriendlyName = "Emby Server",
Manufacturer = "Emby",
@@ -286,7 +282,7 @@ namespace Emby.Dlna.Main
SetProperies(device, fullService);
_Publisher.AddDevice(device);
- var embeddedDevices = new List<string>
+ var embeddedDevices = new []
{
"urn:schemas-upnp-org:service:ContentDirectory:1",
"urn:schemas-upnp-org:service:ConnectionManager:1",
@@ -338,6 +334,11 @@ namespace Emby.Dlna.Main
{
lock (_syncLock)
{
+ if (_manager != null)
+ {
+ return;
+ }
+
try
{
_manager = new PlayToManager(_logger,
@@ -373,6 +374,7 @@ namespace Emby.Dlna.Main
{
try
{
+ _logger.Info("Disposing PlayToManager");
_manager.Dispose();
}
catch (Exception ex)
@@ -386,41 +388,31 @@ namespace Emby.Dlna.Main
public void Dispose()
{
- DisposeDlnaServer();
+ DisposeDevicePublisher();
DisposePlayToManager();
- DisposeSsdpHandler();
+ DisposeDeviceDiscovery();
if (_communicationsServer != null)
{
+ _logger.Info("Disposing SsdpCommunicationsServer");
_communicationsServer.Dispose();
_communicationsServer = null;
}
- GC.SuppressFinalize(this);
+
+ ContentDirectory = null;
+ ConnectionManager = null;
+ MediaReceiverRegistrar = null;
+ Current = null;
}
- public void DisposeDlnaServer()
+ public void DisposeDevicePublisher()
{
if (_Publisher != null)
{
- var devices = _Publisher.Devices.ToList();
- var tasks = devices.Select(i => _Publisher.RemoveDevice(i)).ToArray();
-
- Task.WaitAll(tasks);
- //foreach (var device in devices)
- //{
- // try
- // {
- // _Publisher.RemoveDevice(device);
- // }
- // catch (Exception ex)
- // {
- // _logger.ErrorException("Error sending bye bye", ex);
- // }
- //}
+ _logger.Info("Disposing SsdpDevicePublisher");
_Publisher.Dispose();
+ _Publisher = null;
}
-
- _dlnaServerStarted = false;
}
}
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
index 691ba3061..bce8bfaef 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -7,7 +7,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{
public IEnumerable<ServiceAction> GetActions()
{
- var list = new List<ServiceAction>
+ return new []
{
GetIsValidated(),
GetIsAuthorized(),
@@ -17,8 +17,6 @@ namespace Emby.Dlna.MediaReceiverRegistrar
GetGetValidationRevokedUpdateID(),
GetGetValidationSucceededUpdateID()
};
-
- return list;
}
private ServiceAction GetIsValidated()
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index a617117f3..9a861b8c7 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -106,27 +106,17 @@ namespace Emby.Dlna.PlayTo
_timerFactory = timerFactory;
}
- private int GetPlaybackTimerIntervalMs()
- {
- return 1000;
- }
-
- private int GetInactiveTimerIntervalMs()
- {
- return 60000;
- }
-
public void Start()
{
- _timer = _timerFactory.Create(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
-
- _timerActive = false;
+ _logger.Debug("Dlna Device.Start");
+ _timer = _timerFactory.Create(TimerCallback, null, 1000, Timeout.Infinite);
}
private DateTime _lastVolumeRefresh;
+ private bool _volumeRefreshActive;
private void RefreshVolumeIfNeeded()
{
- if (!_timerActive)
+ if (!_volumeRefreshActive)
{
return;
}
@@ -134,19 +124,19 @@ namespace Emby.Dlna.PlayTo
if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
{
_lastVolumeRefresh = DateTime.UtcNow;
- RefreshVolume();
+ RefreshVolume(CancellationToken.None);
}
}
- private async void RefreshVolume()
+ private async void RefreshVolume(CancellationToken cancellationToken)
{
if (_disposed)
return;
try
{
- await GetVolume().ConfigureAwait(false);
- await GetMute().ConfigureAwait(false);
+ await GetVolume(cancellationToken).ConfigureAwait(false);
+ await GetMute(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -155,21 +145,17 @@ namespace Emby.Dlna.PlayTo
}
private readonly object _timerLock = new object();
- private bool _timerActive;
- private void RestartTimer()
+ private void RestartTimer(bool immediate = false)
{
- if (_disposed)
- return;
-
lock (_timerLock)
{
- if (!_timerActive)
- {
- _logger.Debug("RestartTimer");
- _timer.Change(10, GetPlaybackTimerIntervalMs());
- }
+ if (_disposed)
+ return;
+
+ _volumeRefreshActive = true;
- _timerActive = true;
+ var time = immediate ? 100 : 10000;
+ _timer.Change(time, Timeout.Infinite);
}
}
@@ -178,71 +164,67 @@ namespace Emby.Dlna.PlayTo
/// </summary>
private void RestartTimerInactive()
{
- if (_disposed)
- return;
-
lock (_timerLock)
{
- if (_timerActive)
- {
- _logger.Debug("RestartTimerInactive");
- var interval = GetInactiveTimerIntervalMs();
+ if (_disposed)
+ return;
- if (_timer != null)
- {
- _timer.Change(interval, interval);
- }
- }
+ _volumeRefreshActive = false;
- _timerActive = false;
+ _timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
+ public void OnPlaybackStartedExternally()
+ {
+ RestartTimer(true);
+ }
+
#region Commanding
- public Task VolumeDown()
+ public Task VolumeDown(CancellationToken cancellationToken)
{
var sendVolume = Math.Max(Volume - 5, 0);
- return SetVolume(sendVolume);
+ return SetVolume(sendVolume, cancellationToken);
}
- public Task VolumeUp()
+ public Task VolumeUp(CancellationToken cancellationToken)
{
var sendVolume = Math.Min(Volume + 5, 100);
- return SetVolume(sendVolume);
+ return SetVolume(sendVolume, cancellationToken);
}
- public Task ToggleMute()
+ public Task ToggleMute(CancellationToken cancellationToken)
{
if (IsMuted)
{
- return Unmute();
+ return Unmute(cancellationToken);
}
- return Mute();
+ return Mute(cancellationToken);
}
- public async Task Mute()
+ public async Task Mute(CancellationToken cancellationToken)
{
- var success = await SetMute(true).ConfigureAwait(true);
+ var success = await SetMute(true, cancellationToken).ConfigureAwait(true);
if (!success)
{
- await SetVolume(0).ConfigureAwait(false);
+ await SetVolume(0, cancellationToken).ConfigureAwait(false);
}
}
- public async Task Unmute()
+ public async Task Unmute(CancellationToken cancellationToken)
{
- var success = await SetMute(false).ConfigureAwait(true);
+ var success = await SetMute(false, cancellationToken).ConfigureAwait(true);
if (!success)
{
var sendVolume = _muteVol <= 0 ? 20 : _muteVol;
- await SetVolume(sendVolume).ConfigureAwait(false);
+ await SetVolume(sendVolume, cancellationToken).ConfigureAwait(false);
}
}
@@ -262,9 +244,11 @@ namespace Emby.Dlna.PlayTo
services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase));
}
- private async Task<bool> SetMute(bool mute)
+ private async Task<bool> SetMute(bool mute, CancellationToken cancellationToken)
{
- var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
if (command == null)
return false;
@@ -278,7 +262,7 @@ namespace Emby.Dlna.PlayTo
_logger.Debug("Setting mute");
var value = mute ? 1 : 0;
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
IsMuted = mute;
@@ -289,9 +273,11 @@ namespace Emby.Dlna.PlayTo
/// <summary>
/// Sets volume on a scale of 0-100
/// </summary>
- public async Task SetVolume(int value)
+ public async Task SetVolume(int value, CancellationToken cancellationToken)
{
- var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
if (command == null)
return;
@@ -306,13 +292,15 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
Volume = value;
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
}
- public async Task Seek(TimeSpan value)
+ public async Task Seek(TimeSpan value, CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
if (command == null)
return;
@@ -323,15 +311,21 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+ await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false);
+
+ RestartTimer(true);
}
- public async Task SetAvTransport(string url, string header, string metaData)
+ public async Task SetAvTransport(string url, string header, string metaData, CancellationToken cancellationToken)
{
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ url = url.Replace("&", "&amp;");
+
_logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
if (command == null)
return;
@@ -348,7 +342,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- var post = AvCommands.BuildPost(command, service.ServiceType, url, dictionary);
+ var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
.ConfigureAwait(false);
@@ -356,7 +350,7 @@ namespace Emby.Dlna.PlayTo
try
{
- await SetPlay().ConfigureAwait(false);
+ await SetPlay(avCommands, CancellationToken.None).ConfigureAwait(false);
}
catch
{
@@ -364,7 +358,7 @@ namespace Emby.Dlna.PlayTo
// Others won't
}
- RestartTimer();
+ RestartTimer(true);
}
private string CreateDidlMeta(string value)
@@ -375,11 +369,11 @@ namespace Emby.Dlna.PlayTo
return DescriptionXmlBuilder.Escape(value);
}
- public async Task SetPlay()
+ private Task SetPlay(TransportCommands avCommands, CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
if (command == null)
- return;
+ return Task.CompletedTask;
var service = GetAvTransportService();
@@ -388,52 +382,74 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
- .ConfigureAwait(false);
+ return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
}
- public async Task SetStop()
+ public async Task SetPlay(CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
+
+ RestartTimer(true);
+ }
+
+ public async Task SetStop(CancellationToken cancellationToken)
+ {
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
if (command == null)
return;
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
+
+ RestartTimer(true);
}
- public async Task SetPause()
+ public async Task SetPause(CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
if (command == null)
return;
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
TransportState = TRANSPORTSTATE.PAUSED;
+
+ RestartTimer(true);
}
#endregion
#region Get data
- private int _successiveStopCount;
private int _connectFailureCount;
private async void TimerCallback(object sender)
{
if (_disposed)
return;
- const int maxSuccessiveStopReturns = 5;
-
try
{
- var transportState = await GetTransportInfo().ConfigureAwait(false);
+ var cancellationToken = CancellationToken.None;
+
+ var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ if (avCommands == null)
+ {
+ return;
+ }
+
+ var transportState = await GetTransportInfo(avCommands, cancellationToken).ConfigureAwait(false);
if (_disposed)
{
@@ -451,13 +467,13 @@ namespace Emby.Dlna.PlayTo
}
else
{
- var tuple = await GetPositionInfo().ConfigureAwait(false);
+ var tuple = await GetPositionInfo(avCommands, cancellationToken).ConfigureAwait(false);
var currentObject = tuple.Item2;
if (tuple.Item1 && currentObject == null)
{
- currentObject = await GetMediaInfo().ConfigureAwait(false);
+ currentObject = await GetMediaInfo(avCommands, cancellationToken).ConfigureAwait(false);
}
if (currentObject != null)
@@ -474,16 +490,10 @@ namespace Emby.Dlna.PlayTo
// If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
if (transportState.Value == TRANSPORTSTATE.STOPPED)
{
- _successiveStopCount++;
-
- if (_successiveStopCount >= maxSuccessiveStopReturns)
- {
- RestartTimerInactive();
- }
+ RestartTimerInactive();
}
else
{
- _successiveStopCount = 0;
RestartTimer();
}
}
@@ -492,54 +502,39 @@ namespace Emby.Dlna.PlayTo
RestartTimerInactive();
}
}
- catch (HttpException ex)
+ catch (Exception ex)
{
if (_disposed)
return;
//_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
- _successiveStopCount++;
_connectFailureCount++;
if (_connectFailureCount >= 3)
{
- if (OnDeviceUnavailable != null)
+ var action = OnDeviceUnavailable;
+ if (action != null)
{
_logger.Debug("Disposing device due to loss of connection");
- OnDeviceUnavailable();
+ action();
return;
}
}
- if (_successiveStopCount >= maxSuccessiveStopReturns)
- {
- RestartTimerInactive();
- }
- }
- catch (Exception ex)
- {
- if (_disposed)
- return;
-
- _logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
-
- _successiveStopCount++;
-
- if (_successiveStopCount >= maxSuccessiveStopReturns)
- {
- RestartTimerInactive();
- }
+ RestartTimerInactive();
}
}
- private async Task GetVolume()
+ private async Task GetVolume(CancellationToken cancellationToken)
{
if (_disposed)
{
return;
}
- var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
if (command == null)
return;
@@ -550,7 +545,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), true)
+ var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -570,14 +565,16 @@ namespace Emby.Dlna.PlayTo
}
}
- private async Task GetMute()
+ private async Task GetMute(CancellationToken cancellationToken)
{
if (_disposed)
{
return;
}
- var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
if (command == null)
return;
@@ -588,7 +585,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), true)
+ var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -600,9 +597,9 @@ namespace Emby.Dlna.PlayTo
IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
}
- private async Task<TRANSPORTSTATE?> GetTransportInfo()
+ private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
if (command == null)
return null;
@@ -610,7 +607,7 @@ namespace Emby.Dlna.PlayTo
if (service == null)
return null;
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType), false)
+ var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -634,9 +631,9 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private async Task<uBaseObject> GetMediaInfo()
+ private async Task<uBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
if (command == null)
return null;
@@ -647,7 +644,9 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), false)
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -691,9 +690,9 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private async Task<Tuple<bool, uBaseObject>> GetPositionInfo()
+ private async Task<Tuple<bool, uBaseObject>> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
- var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
+ var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
if (command == null)
return new Tuple<bool, uBaseObject>(false, null);
@@ -704,7 +703,9 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), false)
+ var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+
+ var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -831,42 +832,67 @@ namespace Emby.Dlna.PlayTo
#region From XML
- private async Task GetAVProtocolAsync(CancellationToken cancellationToken)
+ private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
{
+ var avCommands = AvCommands;
+
+ if (avCommands != null)
+ {
+ return avCommands;
+ }
+
if (_disposed)
{
- return;
+ throw new ObjectDisposedException(GetType().Name);
}
var avService = GetAvTransportService();
if (avService == null)
- return;
+ {
+ return null;
+ }
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
+
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
- AvCommands = TransportCommands.Create(document);
+ avCommands = TransportCommands.Create(document);
+ AvCommands = avCommands;
+ return avCommands;
}
- private async Task GetRenderingProtocolAsync(CancellationToken cancellationToken)
+ private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
{
+ var rendererCommands = RendererCommands;
+
+ if (rendererCommands != null)
+ {
+ return rendererCommands;
+ }
+
if (_disposed)
{
- return;
+ throw new ObjectDisposedException(GetType().Name);
}
var avService = GetServiceRenderingControl();
if (avService == null)
- return;
+ {
+ throw new ArgumentException("Device AvService is null");
+ }
+
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
+ _logger.Debug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
- RendererCommands = TransportCommands.Create(document);
+ rendererCommands = TransportCommands.Create(document);
+ RendererCommands = rendererCommands;
+ return rendererCommands;
}
private string NormalizeUrl(string baseUrl, string url)
@@ -891,7 +917,7 @@ namespace Emby.Dlna.PlayTo
set;
}
- internal TransportCommands RendererCommands
+ private TransportCommands RendererCommands
{
get;
set;
@@ -985,12 +1011,6 @@ namespace Emby.Dlna.PlayTo
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
- if (device.GetAvTransportService() != null)
- {
- await device.GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
- await device.GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
- }
-
return device;
}
@@ -1010,8 +1030,8 @@ namespace Emby.Dlna.PlayTo
var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
- var widthValue = int.Parse(width, NumberStyles.Any, UsCulture);
- var heightValue = int.Parse(height, NumberStyles.Any, UsCulture);
+ var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
+ var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
return new DeviceIcon
{
@@ -1089,6 +1109,12 @@ namespace Emby.Dlna.PlayTo
private void OnPlaybackProgress(uBaseObject mediaInfo)
{
+ var mediaUrl = mediaInfo.Url;
+ if (string.IsNullOrWhiteSpace(mediaUrl))
+ {
+ return;
+ }
+
if (PlaybackProgress != null)
{
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
@@ -1131,7 +1157,6 @@ namespace Emby.Dlna.PlayTo
_disposed = true;
DisposeTimer();
- GC.SuppressFinalize(this);
}
}
diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs
index d293bcea1..d453a3d82 100644
--- a/Emby.Dlna/PlayTo/DeviceInfo.cs
+++ b/Emby.Dlna/PlayTo/DeviceInfo.cs
@@ -8,7 +8,6 @@ namespace Emby.Dlna.PlayTo
{
public DeviceInfo()
{
- ClientType = "DLNA";
Name = "Generic Device";
}
@@ -16,8 +15,6 @@ namespace Emby.Dlna.PlayTo
public string Name { get; set; }
- public string ClientType { get; set; }
-
public string ModelName { get; set; }
public string ModelNumber { get; set; }
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index b253cb26e..ce22a0af2 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -21,6 +21,8 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Extensions;
+using System.Net.Http;
+using MediaBrowser.Model.Services;
namespace Emby.Dlna.PlayTo
{
@@ -53,10 +55,6 @@ namespace Emby.Dlna.PlayTo
}
}
- public void OnActivity()
- {
- }
-
public bool SupportsMediaControl
{
get { return IsSessionActive; }
@@ -141,17 +139,15 @@ namespace Emby.Dlna.PlayTo
try
{
- var streamInfo = await StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item != null)
{
- var progress = GetProgressInfo(e.OldMediaInfo, streamInfo);
-
- var positionTicks = progress.PositionTicks;
+ var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks);
}
- streamInfo = await StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item == null) return;
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
@@ -173,20 +169,19 @@ namespace Emby.Dlna.PlayTo
try
{
- var streamInfo = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager)
- .ConfigureAwait(false);
+ var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item == null) return;
- var progress = GetProgressInfo(e.MediaInfo, streamInfo);
-
- var positionTicks = progress.PositionTicks;
+ var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks);
- var duration = streamInfo.MediaSource == null ?
+ var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
+
+ var duration = mediaSource == null ?
(_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) :
- streamInfo.MediaSource.RunTimeTicks;
+ mediaSource.RunTimeTicks;
var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
@@ -241,7 +236,7 @@ namespace Emby.Dlna.PlayTo
try
{
- var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var info = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null)
{
@@ -265,7 +260,14 @@ namespace Emby.Dlna.PlayTo
try
{
- var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var mediaUrl = e.MediaInfo.Url;
+
+ if (string.IsNullOrWhiteSpace(mediaUrl))
+ {
+ return;
+ }
+
+ var info = StreamParams.ParseFromUrl(mediaUrl, _libraryManager, _mediaSourceManager);
if (info.Item != null)
{
@@ -280,7 +282,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
+ private long? GetProgressPositionTicks(uBaseObject mediaInfo, StreamParams info)
{
var ticks = _device.Position.Ticks;
@@ -289,11 +291,16 @@ namespace Emby.Dlna.PlayTo
ticks += info.StartPositionTicks;
}
+ return ticks;
+ }
+
+ private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
+ {
return new PlaybackStartInfo
{
ItemId = info.ItemId,
SessionId = _session.Id,
- PositionTicks = ticks,
+ PositionTicks = GetProgressPositionTicks(mediaInfo, info),
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
MediaSourceId = info.MediaSourceId,
@@ -301,7 +308,8 @@ namespace Emby.Dlna.PlayTo
SubtitleStreamIndex = info.SubtitleStreamIndex,
VolumeLevel = _device.Volume,
- CanSeek = info.MediaSource == null ? _device.Duration.HasValue : info.MediaSource.RunTimeTicks.HasValue,
+ // TODO
+ CanSeek = true,
PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
};
@@ -313,12 +321,12 @@ namespace Emby.Dlna.PlayTo
{
_logger.Debug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
- var user = String.IsNullOrEmpty(command.ControllingUserId) ? null : _userManager.GetUserById(command.ControllingUserId);
+ var user = command.ControllingUserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(command.ControllingUserId);
var items = new List<BaseItem>();
- foreach (string id in command.ItemIds)
+ foreach (var id in command.ItemIds)
{
- AddItemFromId(Guid.Parse(id), items);
+ AddItemFromId(id, items);
}
var startIndex = command.StartIndex ?? 0;
@@ -354,31 +362,31 @@ namespace Emby.Dlna.PlayTo
Playlist.AddRange(playlist);
}
- if (!String.IsNullOrWhiteSpace(command.ControllingUserId))
+ if (!command.ControllingUserId.Equals(Guid.Empty))
{
- await _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
- _session.DeviceName, _session.RemoteEndPoint, user).ConfigureAwait(false);
+ _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
+ _session.DeviceName, _session.RemoteEndPoint, user);
}
await PlayItems(playlist).ConfigureAwait(false);
}
- public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
+ private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
{
switch (command.Command)
{
case PlaystateCommand.Stop:
Playlist.Clear();
- return _device.SetStop();
+ return _device.SetStop(CancellationToken.None);
case PlaystateCommand.Pause:
- return _device.SetPause();
+ return _device.SetPause(CancellationToken.None);
case PlaystateCommand.Unpause:
- return _device.SetPlay();
+ return _device.SetPlay(CancellationToken.None);
case PlaystateCommand.PlayPause:
- return _device.IsPaused ? _device.SetPlay() : _device.SetPause();
+ return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
case PlaystateCommand.Seek:
{
@@ -392,7 +400,7 @@ namespace Emby.Dlna.PlayTo
return SetPlaylistIndex(_currentPlaylistIndex - 1);
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
private async Task Seek(long newPosition)
@@ -401,17 +409,17 @@ namespace Emby.Dlna.PlayTo
if (media != null)
{
- var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null && !EnableClientSideSeek(info))
{
- var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null;
+ var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, info.SubtitleStreamIndex);
- await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false);
+ await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
return;
}
- await SeekAfterTransportChange(newPosition).ConfigureAwait(false);
+ await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
}
}
@@ -425,46 +433,6 @@ namespace Emby.Dlna.PlayTo
return info.IsDirectStream;
}
- public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
#endregion
#region Playlist
@@ -497,13 +465,13 @@ namespace Emby.Dlna.PlayTo
var hasMediaSources = item as IHasMediaSources;
var mediaSources = hasMediaSources != null
- ? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user))
+ ? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
: new List<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
- playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken);
+ playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
@@ -528,7 +496,7 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetAudioChannels,
streamInfo.TargetAudioBitDepth,
streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks,
+ streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
}
@@ -544,10 +512,10 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks,
+ streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
- streamInfo.TargetFramerate,
+ streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic,
@@ -582,8 +550,8 @@ namespace Emby.Dlna.PlayTo
{
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
{
- ItemId = item.Id.ToString("N"),
- MediaSources = mediaSources.ToArray(mediaSources.Count),
+ ItemId = item.Id,
+ MediaSources = mediaSources.ToArray(),
Profile = profile,
DeviceId = deviceId,
MaxBitrate = profile.MaxStreamingBitrate,
@@ -602,7 +570,7 @@ namespace Emby.Dlna.PlayTo
{
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
{
- ItemId = item.Id.ToString("N"),
+ ItemId = item.Id,
MediaSources = mediaSources.ToArray(mediaSources.Count),
Profile = profile,
DeviceId = deviceId,
@@ -642,19 +610,19 @@ namespace Emby.Dlna.PlayTo
if (index < 0 || index >= Playlist.Count)
{
Playlist.Clear();
- await _device.SetStop();
+ await _device.SetStop(CancellationToken.None);
return;
}
_currentPlaylistIndex = index;
var currentitem = Playlist[index];
- await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl);
+ await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, CancellationToken.None);
var streamInfo = currentitem.StreamInfo;
if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
{
- await SeekAfterTransportChange(streamInfo.StartPositionTicks).ConfigureAwait(false);
+ await SeekAfterTransportChange(streamInfo.StartPositionTicks, CancellationToken.None).ConfigureAwait(false);
}
}
@@ -676,13 +644,12 @@ namespace Emby.Dlna.PlayTo
_device.OnDeviceUnavailable = null;
_device.Dispose();
- GC.SuppressFinalize(this);
}
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
+ private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{
GeneralCommandType commandType;
@@ -691,15 +658,15 @@ namespace Emby.Dlna.PlayTo
switch (commandType)
{
case GeneralCommandType.VolumeDown:
- return _device.VolumeDown();
+ return _device.VolumeDown(cancellationToken);
case GeneralCommandType.VolumeUp:
- return _device.VolumeUp();
+ return _device.VolumeUp(cancellationToken);
case GeneralCommandType.Mute:
- return _device.Mute();
+ return _device.Mute(cancellationToken);
case GeneralCommandType.Unmute:
- return _device.Unmute();
+ return _device.Unmute(cancellationToken);
case GeneralCommandType.ToggleMute:
- return _device.ToggleMute();
+ return _device.ToggleMute(cancellationToken);
case GeneralCommandType.SetAudioStreamIndex:
{
string arg;
@@ -708,7 +675,7 @@ namespace Emby.Dlna.PlayTo
{
int val;
- if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val))
+ if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{
return SetAudioStreamIndex(val);
}
@@ -726,7 +693,7 @@ namespace Emby.Dlna.PlayTo
{
int val;
- if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val))
+ if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{
return SetSubtitleStreamIndex(val);
}
@@ -744,9 +711,9 @@ namespace Emby.Dlna.PlayTo
{
int volume;
- if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out volume))
+ if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out volume))
{
- return _device.SetVolume(volume);
+ return _device.SetVolume(volume, cancellationToken);
}
throw new ArgumentException("Unsupported volume value supplied.");
@@ -755,11 +722,11 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentException("Volume argument cannot be null");
}
default:
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
private async Task SetAudioStreamIndex(int? newIndex)
@@ -768,21 +735,20 @@ namespace Emby.Dlna.PlayTo
if (media != null)
{
- var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null)
{
- var progress = GetProgressInfo(media, info);
- var newPosition = progress.PositionTicks ?? 0;
+ var newPosition = GetProgressPositionTicks(media, info) ?? 0;
- var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null;
+ var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
- await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false);
+ await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
if (EnableClientSideSeek(newItem.StreamInfo))
{
- await SeekAfterTransportChange(newPosition).ConfigureAwait(false);
+ await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
}
}
}
@@ -794,27 +760,26 @@ namespace Emby.Dlna.PlayTo
if (media != null)
{
- var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
+ var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null)
{
- var progress = GetProgressInfo(media, info);
- var newPosition = progress.PositionTicks ?? 0;
+ var newPosition = GetProgressPositionTicks(media, info) ?? 0;
- var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null;
+ var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
- await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false);
+ await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
if (EnableClientSideSeek(newItem.StreamInfo) && newPosition > 0)
{
- await SeekAfterTransportChange(newPosition).ConfigureAwait(false);
+ await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
}
}
}
}
- private async Task SeekAfterTransportChange(long positionTicks)
+ private async Task SeekAfterTransportChange(long positionTicks, CancellationToken cancellationToken)
{
const int maxWait = 15000000;
const int interval = 500;
@@ -825,12 +790,12 @@ namespace Emby.Dlna.PlayTo
currentWait += interval;
}
- await _device.Seek(TimeSpan.FromTicks(positionTicks)).ConfigureAwait(false);
+ await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
}
private class StreamParams
{
- public string ItemId { get; set; }
+ public Guid ItemId { get; set; }
public bool IsDirectStream { get; set; }
@@ -847,10 +812,36 @@ namespace Emby.Dlna.PlayTo
public string LiveStreamId { get; set; }
public BaseItem Item { get; set; }
- public MediaSourceInfo MediaSource { get; set; }
+ private MediaSourceInfo MediaSource;
+
+ private IMediaSourceManager _mediaSourceManager;
- private static string GetItemId(string url)
+ public async Task<MediaSourceInfo> GetMediaSource(CancellationToken cancellationToken)
{
+ if (MediaSource != null)
+ {
+ return MediaSource;
+ }
+
+ var hasMediaSources = Item as IHasMediaSources;
+
+ if (hasMediaSources == null)
+ {
+ return null;
+ }
+
+ MediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
+
+ return MediaSource;
+ }
+
+ private static Guid GetItemId(string url)
+ {
+ if (string.IsNullOrEmpty(url))
+ {
+ throw new ArgumentNullException("url");
+ }
+
var parts = url.Split('/');
for (var i = 0; i < parts.Length; i++)
@@ -862,96 +853,108 @@ namespace Emby.Dlna.PlayTo
{
if (parts.Length > i + 1)
{
- return parts[i + 1];
+ return Guid.Parse(parts[i + 1]);
}
}
}
- return null;
+ return Guid.Empty;
}
- public static async Task<StreamParams> ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager)
+ public static StreamParams ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager)
{
+ if (string.IsNullOrEmpty(url))
+ {
+ throw new ArgumentNullException("url");
+ }
+
var request = new StreamParams
{
ItemId = GetItemId(url)
};
- Guid parsedId;
-
- if (string.IsNullOrWhiteSpace(request.ItemId) || !Guid.TryParse(request.ItemId, out parsedId))
+ if (request.ItemId.Equals(Guid.Empty))
{
return request;
}
- const string srch = "params=";
- var index = url.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
-
+ var index = url.IndexOf('?');
if (index == -1) return request;
- var vals = url.Substring(index + srch.Length).Split(';');
+ var query = url.Substring(index + 1);
+ QueryParamCollection values = MyHttpUtility.ParseQueryString(query);
- for (var i = 0; i < vals.Length; i++)
- {
- var val = vals[i];
+ request.DeviceProfileId = values.Get("DeviceProfileId");
+ request.DeviceId = values.Get("DeviceId");
+ request.MediaSourceId = values.Get("MediaSourceId");
+ request.LiveStreamId = values.Get("LiveStreamId");
+ request.IsDirectStream = string.Equals("true", values.Get("Static"), StringComparison.OrdinalIgnoreCase);
- if (string.IsNullOrWhiteSpace(val))
- {
- continue;
- }
+ request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
+ request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
+ request.StartPositionTicks = GetLongValue(values, "StartPositionTicks");
- 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.IsDirectStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- else if (i == 6)
- {
- request.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
- }
- else if (i == 7)
- {
- request.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
- }
- else if (i == 14)
- {
- request.StartPositionTicks = long.Parse(val, CultureInfo.InvariantCulture);
- }
- else if (i == 22)
- {
- request.LiveStreamId = val;
- }
- }
+ request.Item = libraryManager.GetItemById(request.ItemId);
- request.Item = string.IsNullOrWhiteSpace(request.ItemId)
- ? null
- : libraryManager.GetItemById(parsedId);
+ request._mediaSourceManager = mediaSourceManager;
- var hasMediaSources = request.Item as IHasMediaSources;
+ return request;
+ }
+ }
- request.MediaSource = hasMediaSources == null
- ? null
- : (await mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, request.LiveStreamId, false, CancellationToken.None).ConfigureAwait(false));
+ private static int? GetIntValue(QueryParamCollection values, string name)
+ {
+ var value = values.Get(name);
- return request;
+ int result;
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ private static long GetLongValue(QueryParamCollection values, string name)
+ {
+ var value = values.Get(name);
+
+ long result;
+ if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
+ {
+ return result;
}
+
+ return 0;
}
- public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
+ public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
{
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+
+ if (_device == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlayCommand(data as PlayRequest, cancellationToken);
+ }
+ if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
+ }
+ if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendGeneralCommand(data as GeneralCommand, cancellationToken);
+ }
+
// Not supported or needed right now
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
}
}
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 84094d906..ed3cf311b 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -19,6 +19,8 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using System.Threading;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Devices;
namespace Emby.Dlna.PlayTo
{
@@ -42,8 +44,6 @@ namespace Emby.Dlna.PlayTo
private readonly IMediaEncoder _mediaEncoder;
private readonly ITimerFactory _timerFactory;
- private readonly List<string> _nonRendererUrls = new List<string>();
- private DateTime _lastRendererClear;
private bool _disposed;
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
@@ -122,8 +122,6 @@ namespace Emby.Dlna.PlayTo
catch (Exception ex)
{
_logger.ErrorException("Error creating PlayTo device.", ex);
-
- _nonRendererUrls.Add(location);
}
finally
{
@@ -131,64 +129,88 @@ namespace Emby.Dlna.PlayTo
}
}
- private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
+ private string GetUuid(string usn)
{
- if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
+ var found = false;
+ var index = usn.IndexOf("uuid:", StringComparison.OrdinalIgnoreCase);
+ if (index != -1)
+ {
+ usn = usn.Substring(index);
+ found = true;
+ }
+ index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
+ if (index != -1)
{
- _nonRendererUrls.Clear();
- _lastRendererClear = DateTime.UtcNow;
+ usn = usn.Substring(0, index);
}
- if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
+ if (found)
{
- return;
+ return usn;
}
+ return usn.GetMD5().ToString("N");
+ }
+
+ private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
+ {
var uri = info.Location;
_logger.Debug("Attempting to create PlayToController from location {0}", location);
- var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
- if (device.RendererCommands == null)
+ _logger.Debug("Logging session activity from location {0}", location);
+ string uuid;
+ if (info.Headers.TryGetValue("USN", out uuid))
{
- //_logger.Debug("Upnp device {0} does not contain a MediaRenderer device (1).", location);
- _nonRendererUrls.Add(location);
- return;
+ uuid = GetUuid(uuid);
+ }
+ else
+ {
+ uuid = location.GetMD5().ToString("N");
}
- _logger.Debug("Logging session activity from location {0}", location);
- var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null).ConfigureAwait(false);
+ string deviceName = null;
- var controller = sessionInfo.SessionController as PlayToController;
+ var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion.ToString(), uuid, deviceName, uri.OriginalString, null);
+
+ var controller = sessionInfo.SessionControllers.OfType<PlayToController>().FirstOrDefault();
if (controller == null)
{
+ var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
+
+ deviceName = device.Properties.Name;
+
+ _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
+
string serverAddress;
- if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback))
+ if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Any))
{
- serverAddress = await GetServerAddress(null, cancellationToken).ConfigureAwait(false);
+ serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
}
else
{
- serverAddress = await GetServerAddress(info.LocalIpAddress, cancellationToken).ConfigureAwait(false);
+ serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
}
string accessToken = null;
- sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
- _sessionManager,
- _libraryManager,
- _logger,
- _dlnaManager,
- _userManager,
- _imageProcessor,
- serverAddress,
- accessToken,
- _deviceDiscovery,
- _userDataManager,
- _localization,
- _mediaSourceManager,
- _config,
- _mediaEncoder);
+ controller = new PlayToController(sessionInfo,
+ _sessionManager,
+ _libraryManager,
+ _logger,
+ _dlnaManager,
+ _userManager,
+ _imageProcessor,
+ serverAddress,
+ accessToken,
+ _deviceDiscovery,
+ _userDataManager,
+ _localization,
+ _mediaSourceManager,
+ _config,
+ _mediaEncoder);
+
+ sessionInfo.AddController(controller);
controller.Init(device);
@@ -208,31 +230,21 @@ namespace Emby.Dlna.PlayTo
GeneralCommandType.ToggleMute.ToString(),
GeneralCommandType.SetVolume.ToString(),
GeneralCommandType.SetAudioStreamIndex.ToString(),
- GeneralCommandType.SetSubtitleStreamIndex.ToString()
+ GeneralCommandType.SetSubtitleStreamIndex.ToString(),
+ GeneralCommandType.PlayMediaSource.ToString()
},
- SupportsMediaControl = true,
-
- // xbox one creates a new uuid everytime it restarts
- SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
+ SupportsMediaControl = true
});
_logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
}
}
- private Task<string> GetServerAddress(IpAddressInfo address, CancellationToken cancellationToken)
- {
- if (address == null)
- {
- return _appHost.GetLocalApiUrl(cancellationToken);
- }
-
- return Task.FromResult(_appHost.GetLocalApiUrl(address));
- }
-
public void Dispose()
{
+ _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
+
try
{
_disposeCancellationTokenSource.Cancel();
@@ -243,8 +255,6 @@ namespace Emby.Dlna.PlayTo
}
_disposed = true;
- _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
index d31dc155e..e2d6e43c0 100644
--- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -18,7 +18,7 @@ namespace Emby.Dlna.PlayTo
{
StreamInfo = new StreamInfo
{
- ItemId = item.Id.ToString("N"),
+ ItemId = item.Id,
MediaType = DlnaProfileType.Photo,
DeviceProfile = profile
},
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index d4b594367..eaafaa65b 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -32,7 +32,9 @@ namespace Emby.Dlna.PlayTo
bool logRequest = true,
string header = null)
{
- using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest)
+ var cancellationToken = CancellationToken.None;
+
+ using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest, cancellationToken)
.ConfigureAwait(false))
{
using (var stream = response.Content)
@@ -123,7 +125,8 @@ namespace Emby.Dlna.PlayTo
string soapAction,
string postData,
string header,
- bool logRequest)
+ bool logRequest,
+ CancellationToken cancellationToken)
{
if (!soapAction.StartsWith("\""))
soapAction = "\"" + soapAction + "\"";
@@ -137,14 +140,16 @@ namespace Emby.Dlna.PlayTo
BufferContent = false,
// The periodic requests may keep some devices awake
- LogRequestAsDebug = true
+ LogRequestAsDebug = true,
+
+ CancellationToken = cancellationToken
};
options.RequestHeaders["SOAPAction"] = soapAction;
options.RequestHeaders["Pragma"] = "no-cache";
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
- if (!string.IsNullOrWhiteSpace(header))
+ if (!string.IsNullOrEmpty(header))
{
options.RequestHeaders["contentFeatures.dlna.org"] = header;
}
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index d7d44573c..9e055f792 100644
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -91,7 +91,7 @@ namespace Emby.Dlna.PlayTo
};
}
- public static StateVariable FromXml(XElement container)
+ private static StateVariable FromXml(XElement container)
{
var allowedValues = new List<string>();
var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
@@ -108,7 +108,7 @@ namespace Emby.Dlna.PlayTo
{
Name = container.GetValue(uPnpNamespaces.svc + "name"),
DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
- AllowedValues = allowedValues
+ AllowedValues = allowedValues.ToArray()
};
}
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index 75204b234..4007d8870 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -5,23 +5,21 @@ using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class DefaultProfile : DeviceProfile
{
public DefaultProfile()
{
Name = "Generic Device";
- ProtocolInfo = "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000";
-
- XDlnaDoc = "DMS-1.50";
+ ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*";
Manufacturer = "Emby";
- ModelDescription = "Emby";
+ ModelDescription = "UPnP/AV 1.0 Compliant Media Server";
ModelName = "Emby Server";
- ModelNumber = "Emby";
- ModelUrl = "http://emby.media/";
- ManufacturerUrl = "http://emby.media/";
+ ModelNumber = "01";
+ ModelUrl = "https://emby.media";
+ ManufacturerUrl = "https://emby.media";
AlbumArtPn = "JPEG_SM";
@@ -31,8 +29,8 @@ namespace Emby.Dlna.Profiles
MaxIconWidth = 48;
MaxIconHeight = 48;
- MaxStreamingBitrate = 40000000;
- MaxStaticBitrate = 40000000;
+ MaxStreamingBitrate = 140000000;
+ MaxStaticBitrate = 140000000;
MusicStreamingTranscodingBitrate = 192000;
EnableAlbumArtInDidl = false;
diff --git a/Emby.Dlna/Profiles/DenonAvrProfile.cs b/Emby.Dlna/Profiles/DenonAvrProfile.cs
index 15da48108..b8a44396a 100644
--- a/Emby.Dlna/Profiles/DenonAvrProfile.cs
+++ b/Emby.Dlna/Profiles/DenonAvrProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class DenonAvrProfile : DefaultProfile
{
public DenonAvrProfile()
diff --git a/Emby.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs
index bb9ce903c..4243c1c9d 100644
--- a/Emby.Dlna/Profiles/DirectTvProfile.cs
+++ b/Emby.Dlna/Profiles/DirectTvProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class DirectTvProfile : DefaultProfile
{
public DirectTvProfile()
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index 3f779c679..c1d1eede2 100644
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class DishHopperJoeyProfile : DefaultProfile
{
public DishHopperJoeyProfile()
diff --git a/Emby.Dlna/Profiles/Foobar2000Profile.cs b/Emby.Dlna/Profiles/Foobar2000Profile.cs
index 915c49048..69a5d05dd 100644
--- a/Emby.Dlna/Profiles/Foobar2000Profile.cs
+++ b/Emby.Dlna/Profiles/Foobar2000Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class Foobar2000Profile : DefaultProfile
{
public Foobar2000Profile()
diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
index 33185365c..79cf2ebfd 100644
--- a/Emby.Dlna/Profiles/LgTvProfile.cs
+++ b/Emby.Dlna/Profiles/LgTvProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class LgTvProfile : DefaultProfile
{
public LgTvProfile()
@@ -53,7 +53,7 @@ namespace Emby.Dlna.Profiles
{
new DirectPlayProfile
{
- Container = "ts,mpegts,avi,mkv",
+ Container = "ts,mpegts,avi,mkv,m2ts",
VideoCodec = "h264",
AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
Type = DlnaProfileType.Video
@@ -153,12 +153,6 @@ namespace Emby.Dlna.Profiles
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
@@ -203,6 +197,12 @@ namespace Emby.Dlna.Profiles
Container = "m4v",
Type = DlnaProfileType.Video,
MimeType = "video/mp4"
+ },
+ new ResponseProfile
+ {
+ Container = "ts,mpegts",
+ Type = DlnaProfileType.Video,
+ MimeType = "video/mpeg"
}
};
}
diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
index 83236c594..ffb735d8d 100644
--- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class LinksysDMA2100Profile : DefaultProfile
{
public LinksysDMA2100Profile()
diff --git a/Emby.Dlna/Profiles/MarantzProfile.cs b/Emby.Dlna/Profiles/MarantzProfile.cs
index 0f9f06b88..b5b486a9c 100644
--- a/Emby.Dlna/Profiles/MarantzProfile.cs
+++ b/Emby.Dlna/Profiles/MarantzProfile.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class MarantzProfile : DefaultProfile
{
public MarantzProfile()
diff --git a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
index 1ab4bcf82..441933efa 100644
--- a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
+++ b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class MediaMonkeyProfile : DefaultProfile
{
public MediaMonkeyProfile()
diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
index a267158c9..53d6a62bb 100644
--- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
+++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class PanasonicVieraProfile : DefaultProfile
{
public PanasonicVieraProfile()
diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
index 33270e72a..b91089b1b 100644
--- a/Emby.Dlna/Profiles/PopcornHourProfile.cs
+++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class PopcornHourProfile : DefaultProfile
{
public PopcornHourProfile()
diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
index cd9056632..b55146ffd 100644
--- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SamsungSmartTvProfile : DefaultProfile
{
public SamsungSmartTvProfile()
@@ -42,7 +42,7 @@ namespace Emby.Dlna.Profiles
},
new TranscodingProfile
{
- Container = "ts,mpegts",
+ Container = "ts",
AudioCodec = "ac3",
VideoCodec = "h264",
Type = DlnaProfileType.Video,
diff --git a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
index b49ad0197..d27a0e782 100644
--- a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SharpSmartTvProfile : DefaultProfile
{
public SharpSmartTvProfile()
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
index ac7f56b46..dd2ca7a7d 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2013 : DefaultProfile
{
public SonyBlurayPlayer2013()
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
index 961ff30f2..69a2f2b61 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2014 : DefaultProfile
{
public SonyBlurayPlayer2014()
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
index 2573121b1..ef443e56e 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2015 : DefaultProfile
{
public SonyBlurayPlayer2015()
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
index 1ffed3d62..92e5cb086 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2016 : DefaultProfile
{
public SonyBlurayPlayer2016()
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
index ddda638ed..2bf65351e 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayerProfile : DefaultProfile
{
public SonyBlurayPlayerProfile()
diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
index 0986ebdcb..75382067f 100644
--- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2010Profile : DefaultProfile
{
public SonyBravia2010Profile()
diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
index ff8316d9b..75fefa9a8 100644
--- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2011Profile : DefaultProfile
{
public SonyBravia2011Profile()
diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
index c592fae5c..1d55d91f9 100644
--- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2012Profile : DefaultProfile
{
public SonyBravia2012Profile()
diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
index 8580c744d..555ce419b 100644
--- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2013Profile : DefaultProfile
{
public SonyBravia2013Profile()
diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
index 8f871dee7..b6e5bbea5 100644
--- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2014Profile : DefaultProfile
{
public SonyBravia2014Profile()
diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
index 69b0f81ee..fc69d2dd3 100644
--- a/Emby.Dlna/Profiles/SonyPs3Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyPs3Profile : DefaultProfile
{
public SonyPs3Profile()
@@ -35,7 +35,6 @@ namespace Emby.Dlna.Profiles
AlbumArtPn = "JPEG_TN";
SonyAggregationFlags = "10";
- XDlnaDoc = "DMS-1.50";
EnableSingleAlbumArtLimit = true;
DirectPlayProfiles = new[]
diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
index 4c4c1f676..9b444ec24 100644
--- a/Emby.Dlna/Profiles/SonyPs4Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyPs4Profile : DefaultProfile
{
public SonyPs4Profile()
@@ -35,7 +35,6 @@ namespace Emby.Dlna.Profiles
AlbumArtPn = "JPEG_TN";
SonyAggregationFlags = "10";
- XDlnaDoc = "DMS-1.50";
EnableSingleAlbumArtLimit = true;
DirectPlayProfiles = new[]
diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
index b4ca5a3d9..bab557674 100644
--- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs
+++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class WdtvLiveProfile : DefaultProfile
{
public WdtvLiveProfile()
diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
index d497ee161..46740cb31 100644
--- a/Emby.Dlna/Profiles/XboxOneProfile.cs
+++ b/Emby.Dlna/Profiles/XboxOneProfile.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles
{
- [XmlRoot("Profile")]
+ [System.Xml.Serialization.XmlRoot("Profile")]
public class XboxOneProfile : DefaultProfile
{
public XboxOneProfile()
diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml
index 77392d9ce..cdbf3bbe5 100644
--- a/Emby.Dlna/Profiles/Xml/Default.xml
+++ b/Emby.Dlna/Profiles/Xml/Default.xml
@@ -2,11 +2,11 @@
<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Generic Device</Name>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -16,12 +16,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
index 3ab20c330..14e89d006 100644
--- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
@@ -7,11 +7,11 @@
<Headers />
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -21,12 +21,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
@@ -42,7 +41,14 @@
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
- <CodecProfiles />
+ <CodecProfiles>
+ <CodecProfile type="Audio" container="flac">
+ <Conditions>
+ <ProfileCondition condition="LessThanEqual" property="AudioSampleRate" value="96000" isRequired="true" />
+ </Conditions>
+ <ApplyConditions />
+ </CodecProfile>
+ </CodecProfiles>
<ResponseProfiles />
<SubtitleProfiles>
<SubtitleProfile format="srt" method="External" />
diff --git a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
index f022191a3..cfd2edfae 100644
--- a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
+++ b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
<RequiresPlainVideoItems>true</RequiresPlainVideoItems>
<RequiresPlainFolders>true</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
index fa8fd0e74..d95b8dc96 100644
--- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
+++ b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
index 845ff0415..1c579321b 100644
--- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
@@ -35,7 +34,7 @@
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<XmlRootAttributes />
<DirectPlayProfiles>
- <DirectPlayProfile container="ts,mpegts,avi,mkv" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264" type="Video" />
+ <DirectPlayProfile container="ts,mpegts,avi,mkv,m2ts" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264" type="Video" />
<DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="mp3" type="Audio" />
<DirectPlayProfile container="jpeg" type="Photo" />
@@ -66,7 +65,6 @@
<Conditions>
<ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
<ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
- <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
<ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
</Conditions>
<ApplyConditions />
@@ -82,6 +80,9 @@
<ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
<Conditions />
</ResponseProfile>
+ <ResponseProfile container="ts,mpegts" type="Video" mimeType="video/mpeg">
+ <Conditions />
+ </ResponseProfile>
</ResponseProfiles>
<SubtitleProfiles>
<SubtitleProfile format="srt" method="Embed" />
diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
index 410a79432..d98f2bb07 100644
--- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
@@ -6,11 +6,11 @@
<Headers />
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -20,12 +20,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Marantz.xml b/Emby.Dlna/Profiles/Xml/Marantz.xml
index 7d2d53ee4..81bea492f 100644
--- a/Emby.Dlna/Profiles/Xml/Marantz.xml
+++ b/Emby.Dlna/Profiles/Xml/Marantz.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
index 944b80f71..a144b3475 100644
--- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
index 0b842fe2a..47a130444 100644
--- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,12 +23,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>10</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
index c49184a4c..a695de8fd 100644
--- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
+++ b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
@@ -2,11 +2,11 @@
<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Popcorn Hour</Name>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -16,12 +16,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
index 361d7660b..6f7a59efc 100644
--- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
@@ -51,7 +50,7 @@
</DirectPlayProfiles>
<TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
- <TranscodingProfile container="ts,mpegts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
index 9043330ec..73c4d2ff5 100644
--- a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>true</RequiresPlainVideoItems>
<RequiresPlainFolders>true</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
index 2734aec9d..9219569b8 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
@@ -12,11 +12,11 @@
</Headers>
</Identification>
<Manufacturer>Microsoft Corporation</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -26,11 +26,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
index 8c5e0a90e..d7a53832d 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
@@ -12,11 +12,11 @@
</Headers>
</Identification>
<Manufacturer>Microsoft Corporation</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -26,11 +26,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
index 6d55ef980..316aa6de2 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
@@ -10,11 +10,11 @@
</Headers>
</Identification>
<Manufacturer>Microsoft Corporation</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -24,11 +24,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
index 58312c1c6..f6e93a191 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
@@ -10,11 +10,11 @@
</Headers>
</Identification>
<Manufacturer>Microsoft Corporation</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -24,11 +24,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
index 011ba08ea..f9212cccb 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
@@ -10,11 +10,11 @@
</Headers>
</Identification>
<Manufacturer>Microsoft Corporation</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -24,11 +24,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
index c99e21a34..d84531758 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
@@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
<ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
index 6defa3c22..8e376fbf9 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
@@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
index d90f02a55..92a04dc7a 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
@@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
index ad5d1a676..953ac0a43 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
@@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
index f654982cb..34afe7e6e 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
@@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
- <ModelDescription>Emby</ModelDescription>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
index 8da880951..d603010d0 100644
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
index c192d082e..9540debff 100644
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,13 +23,12 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
index 0fc8b9973..79d9ba2ce 100644
--- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml
+++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,12 +23,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>5</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml
index 0b095b2d0..b05b21455 100644
--- a/Emby.Dlna/Profiles/Xml/Xbox One.xml
+++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml
@@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -23,12 +23,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>40</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml
index 82ffc916c..56c04e1e7 100644
--- a/Emby.Dlna/Profiles/Xml/foobar2000.xml
+++ b/Emby.Dlna/Profiles/Xml/foobar2000.xml
@@ -8,11 +8,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
- <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
- <ModelDescription>Emby</ModelDescription>
- <ModelNumber>Emby</ModelNumber>
- <ModelUrl>http://emby.media/</ModelUrl>
+ <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
+ <ModelNumber>01</ModelNumber>
+ <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@@ -22,12 +22,11 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
- <MaxStaticBitrate>40000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
- <XDlnaDoc>DMS-1.50</XDlnaDoc>
- <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index bba4adc5f..988d45e07 100644
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -22,12 +22,12 @@ namespace Emby.Dlna.Server
public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
{
- if (string.IsNullOrWhiteSpace(serverUdn))
+ if (string.IsNullOrEmpty(serverUdn))
{
throw new ArgumentNullException("serverUdn");
}
- if (string.IsNullOrWhiteSpace(serverAddress))
+ if (string.IsNullOrEmpty(serverAddress))
{
throw new ArgumentNullException("serverAddress");
}
@@ -77,6 +77,11 @@ namespace Emby.Dlna.Server
builder.Append("<minor>0</minor>");
builder.Append("</specVersion>");
+ if (!EnableAbsoluteUrls)
+ {
+ builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
+ }
+
AppendDeviceInfo(builder);
builder.Append("</root>");
@@ -90,6 +95,9 @@ namespace Emby.Dlna.Server
AppendDeviceProperties(builder);
AppendIconList(builder);
+
+ builder.Append("<presentationURL>" + Escape(_serverAddress) + "/web/index.html</presentationURL>");
+
AppendServiceList(builder);
builder.Append("</device>");
}
@@ -169,12 +177,12 @@ namespace Emby.Dlna.Server
private void AppendDeviceProperties(StringBuilder builder)
{
- builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
-
- builder.Append("<dlna:X_DLNACAP>" + Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
+ builder.Append("<dlna:X_DLNACAP/>");
+ builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">DMS-1.50</dlna:X_DLNADOC>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
- builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
+
+ builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
builder.Append("<friendlyName>" + Escape(GetFriendlyName()) + "</friendlyName>");
builder.Append("<manufacturer>" + Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");
@@ -186,7 +194,7 @@ namespace Emby.Dlna.Server
builder.Append("<modelNumber>" + Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>");
builder.Append("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
- if (string.IsNullOrWhiteSpace(_profile.SerialNumber))
+ if (string.IsNullOrEmpty(_profile.SerialNumber))
{
builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>");
}
@@ -195,15 +203,11 @@ namespace Emby.Dlna.Server
builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>");
}
- builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
- builder.Append("<presentationURL>" + Escape(_serverAddress) + "</presentationURL>");
+ builder.Append("<UPC/>");
- if (!EnableAbsoluteUrls)
- {
- //builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
- }
+ builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
- if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
+ if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
{
builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");
}
@@ -211,7 +215,7 @@ namespace Emby.Dlna.Server
private string GetFriendlyName()
{
- if (string.IsNullOrWhiteSpace(_profile.FriendlyName))
+ if (string.IsNullOrEmpty(_profile.FriendlyName))
{
return "Emby - " + _serverName;
}
@@ -226,7 +230,7 @@ namespace Emby.Dlna.Server
}
}
- var characters = characterList.ToArray(characterList.Count);
+ var characters = characterList.ToArray();
var serverName = new string(characters);
@@ -277,7 +281,7 @@ namespace Emby.Dlna.Server
private string BuildUrl(string url)
{
- if (string.IsNullOrWhiteSpace(url))
+ if (string.IsNullOrEmpty(url))
{
return string.Empty;
}
diff --git a/Emby.Dlna/Server/UpnpDevice.cs b/Emby.Dlna/Server/UpnpDevice.cs
deleted file mode 100644
index 46f3d1c83..000000000
--- a/Emby.Dlna/Server/UpnpDevice.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-using System.Net;
-using MediaBrowser.Model.Net;
-
-namespace Emby.Dlna.Server
-{
- public sealed class UpnpDevice
- {
- public readonly Uri Descriptor;
- public readonly string Type;
- public readonly string USN;
- public readonly string Uuid;
- public readonly IpAddressInfo Address;
-
- public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IpAddressInfo address)
- {
- Uuid = aUuid;
- Type = aType;
- Descriptor = aDescriptor;
-
- Address = address;
-
- USN = CreateUSN(aUuid, aType);
- }
-
- private static string CreateUSN(string aUuid, string aType)
- {
- if (aType.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
- {
- return aType;
- }
- else
- {
- return String.Format("uuid:{0}::{1}", aUuid, aType);
- }
- }
- }
-}
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 7cd10bd01..b2b742b10 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -255,7 +255,7 @@ namespace Emby.Dlna.Service
}
var originalHeaders = response.Headers;
- var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count));
+ var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
//builder.Append(response.Xml);
Logger.Debug("Control response. Headers: {0}", headers);
diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
index 08eb80403..c41f1b3be 100644
--- a/Emby.Dlna/Service/ServiceXmlBuilder.cs
+++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs
@@ -72,7 +72,7 @@ namespace Emby.Dlna.Service
builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>");
builder.Append("<dataType>" + DescriptionXmlBuilder.Escape(item.DataType ?? string.Empty) + "</dataType>");
- if (item.AllowedValues.Count > 0)
+ if (item.AllowedValues.Length > 0)
{
builder.Append("<allowedValueList>");
foreach (var allowedValue in item.AllowedValues)
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index bd5ad31c2..a75e065c3 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -26,13 +26,38 @@ namespace Emby.Dlna.Ssdp
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
+ private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
+
+ private int _listenerCount;
+ private object _syncLock = new object();
+ public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
+ {
+ add
+ {
+ lock (_syncLock)
+ {
+ _listenerCount++;
+ DeviceDiscoveredInternal += value;
+ }
+ StartInternal();
+ }
+ remove
+ {
+ lock (_syncLock)
+ {
+ _listenerCount--;
+ DeviceDiscoveredInternal -= value;
+ }
+ }
+ }
+
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
private SsdpDeviceLocator _deviceLocator;
private readonly ITimerFactory _timerFactory;
private readonly ISocketFactory _socketFactory;
+ private ISsdpCommunicationsServer _commsServer;
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, ISocketFactory socketFactory, ITimerFactory timerFactory)
{
@@ -45,21 +70,34 @@ namespace Emby.Dlna.Ssdp
// Call this method from somewhere in your code to start the search.
public void Start(ISsdpCommunicationsServer communicationsServer)
{
- _deviceLocator = new SsdpDeviceLocator(communicationsServer, _timerFactory);
+ _commsServer = communicationsServer;
+
+ StartInternal();
+ }
+
+ private void StartInternal()
+ {
+ lock (_syncLock)
+ {
+ if (_listenerCount > 0 && _deviceLocator == null)
+ {
+ _deviceLocator = new SsdpDeviceLocator(_commsServer, _timerFactory);
- // (Optional) Set the filter so we only see notifications for devices we care about
- // (can be any search target value i.e device type, uuid value etc - any value that appears in the
- // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
- //_DeviceLocator.NotificationFilter = "upnp:rootdevice";
+ // (Optional) Set the filter so we only see notifications for devices we care about
+ // (can be any search target value i.e device type, uuid value etc - any value that appears in the
+ // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
+ //_DeviceLocator.NotificationFilter = "upnp:rootdevice";
- // Connect our event handler so we process devices as they are found
- _deviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
- _deviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
+ // Connect our event handler so we process devices as they are found
+ _deviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
+ _deviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
- var dueTime = TimeSpan.FromSeconds(5);
- var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
+ var dueTime = TimeSpan.FromSeconds(5);
+ var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
- _deviceLocator.RestartBroadcastTimer(dueTime, interval);
+ _deviceLocator.RestartBroadcastTimer(dueTime, interval);
+ }
+ }
}
// Process each found device in the event handler
@@ -81,7 +119,7 @@ namespace Emby.Dlna.Ssdp
}
};
- EventHelper.FireEventIfNotNull(DeviceDiscovered, this, args, _logger);
+ EventHelper.FireEventIfNotNull(DeviceDiscoveredInternal, this, args, _logger);
}
private void _DeviceLocator_DeviceUnavailable(object sender, DeviceUnavailableEventArgs e)
diff --git a/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj b/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj
index c4cef629a..8f3a70f5b 100644
--- a/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj
+++ b/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj
@@ -1,82 +1,22 @@
-<?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')" />
+<Project Sdk="Microsoft.NET.Sdk">
+
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{6CFEE013-6E7C-432B-AC37-CABF0880C69A}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Drawing.ImageMagick</RootNamespace>
- <AssemblyName>Emby.Drawing.ImageMagick</AssemblyName>
- <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- </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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
+
<ItemGroup>
- <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\ImageMagickSharp.1.0.0.19\lib\net45\ImageMagickSharp.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <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" />
+ <PackageReference Include="ImageMagickSharp" Version="1.0.0.20" />
</ItemGroup>
+
<ItemGroup>
- <Compile Include="ImageHelpers.cs" />
- <Compile Include="ImageMagickEncoder.cs" />
- <Compile Include="PercentPlayedDrawer.cs" />
- <Compile Include="PlayedIndicatorDrawer.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="StripCollageBuilder.cs" />
- <Compile Include="UnplayedCountIndicator.cs" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
+
<ItemGroup>
<EmbeddedResource Include="fonts\robotoregular.ttf" />
</ItemGroup>
- <ItemGroup>
- <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>
- </ItemGroup>
- <ItemGroup>
- <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
+
+</Project>
diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
index 3b3f7cf0a..9a313d9d3 100644
--- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
+++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
@@ -138,7 +138,7 @@ namespace Emby.Drawing.ImageMagick
// Even if the caller specified 100, don't use it because it takes forever
quality = Math.Min(quality, 99);
- if (string.IsNullOrWhiteSpace(options.BackgroundColor) || !HasTransparency(inputPath))
+ if (string.IsNullOrEmpty(options.BackgroundColor) || !HasTransparency(inputPath))
{
using (var originalImage = new MagickWand(inputPath))
{
@@ -216,7 +216,7 @@ namespace Emby.Drawing.ImageMagick
private void AddForegroundLayer(MagickWand wand, ImageProcessingOptions options)
{
- if (string.IsNullOrWhiteSpace(options.ForegroundLayer))
+ if (string.IsNullOrEmpty(options.ForegroundLayer))
{
return;
}
@@ -328,7 +328,6 @@ namespace Emby.Drawing.ImageMagick
{
_disposed = true;
Wand.CloseEnvironment();
- GC.SuppressFinalize(this);
}
private void CheckDisposed()
diff --git a/Emby.Drawing.ImageMagick/packages.config b/Emby.Drawing.ImageMagick/packages.config
deleted file mode 100644
index aaf7ab7ba..000000000
--- a/Emby.Drawing.ImageMagick/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="ImageMagickSharp" version="1.0.0.19" targetFramework="net452" />
-</packages> \ No newline at end of file
diff --git a/Emby.Drawing.Net/Emby.Drawing.Net.csproj b/Emby.Drawing.Net/Emby.Drawing.Net.csproj
index 16e72a085..dc9d35dca 100644
--- a/Emby.Drawing.Net/Emby.Drawing.Net.csproj
+++ b/Emby.Drawing.Net/Emby.Drawing.Net.csproj
@@ -1,78 +1,17 @@
-<?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>{C97A239E-A96C-4D64-A844-CCF8CC30AECB}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Drawing.Net</RootNamespace>
- <AssemblyName>Emby.Drawing.Net</AssemblyName>
- <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- </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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Drawing" />
- <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="DynamicImageHelpers.cs" />
- <Compile Include="GDIImageEncoder.cs" />
- <Compile Include="ImageExtensions.cs" />
- <Compile Include="ImageHelpers.cs" />
- <Compile Include="PercentPlayedDrawer.cs" />
- <Compile Include="PlayedIndicatorDrawer.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="UnplayedCountIndicator.cs" />
- </ItemGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <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="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+
<ItemGroup>
<EmbeddedResource Include="empty.png" />
</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
+
+</Project>
diff --git a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
index 2b61561cb..55dbf876a 100644
--- a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
+++ b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
@@ -1,78 +1,22 @@
-<?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')" />
+<Project Sdk="Microsoft.NET.Sdk">
+
<PropertyGroup>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{2312DA6D-FF86-4597-9777-BCEEC32D96DD}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Drawing.Skia</RootNamespace>
- <AssemblyName>Emby.Drawing.Skia</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <ItemGroup>
- <!-- A reference to the entire .NET Framework is automatically included -->
- <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>
- </ItemGroup>
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="PercentPlayedDrawer.cs" />
- <Compile Include="PlayedIndicatorDrawer.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="SkiaEncoder.cs" />
- <Compile Include="StripCollageBuilder.cs" />
- <Compile Include="UnplayedCountIndicator.cs" />
+ <PackageReference Include="SkiaSharp" Version="1.60.3" />
</ItemGroup>
+
<ItemGroup>
- <Reference Include="SkiaSharp, Version=1.58.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
- <HintPath>..\packages\SkiaSharp.1.58.1\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll</HintPath>
- </Reference>
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
+
<ItemGroup>
- <None Include="packages.config" />
+ <Compile Include="..\SharedVersion.cs" />
</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
+
+</Project>
diff --git a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs b/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
index 2417043d6..a4d8a2938 100644
--- a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
+++ b/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
@@ -2,14 +2,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Drawing;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
-using System.Reflection;
-using MediaBrowser.Common.Progress;
namespace Emby.Drawing.Skia
{
@@ -28,7 +21,7 @@ namespace Emby.Drawing.Skia
_fileSystem = fileSystem;
}
- public async Task DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
+ public void DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
{
var x = imageSize.Width - OffsetFromTopRightCorner;
@@ -61,35 +54,5 @@ namespace Emby.Drawing.Skia
canvas.DrawText(text, (float)x-20, OffsetFromTopRightCorner + 12, paint);
}
}
-
- internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)
- {
- var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
-
- if (fileSystem.FileExists(filePath))
- {
- return filePath;
- }
-
- var tempPath = await httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- Progress = new SimpleProgress<double>()
-
- }).ConfigureAwait(false);
-
- fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
-
- try
- {
- fileSystem.CopyFile(tempPath, filePath, false);
- }
- catch (IOException)
- {
-
- }
-
- return tempPath;
- }
}
}
diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs
index 9b4f1fc58..7ccb75ec4 100644
--- a/Emby.Drawing.Skia/SkiaEncoder.cs
+++ b/Emby.Drawing.Skia/SkiaEncoder.cs
@@ -57,9 +57,13 @@ namespace Emby.Drawing.Skia
"pkm",
"wbmp",
+ // TODO
+ // Are all of these supported? https://github.com/google/skia/blob/master/infra/bots/recipes/test.py#L454
+
// working on windows at least
"cr2",
- "nef"
+ "nef",
+ "arw"
};
}
}
@@ -68,7 +72,7 @@ namespace Emby.Drawing.Skia
{
get
{
- return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png, ImageFormat.Bmp };
+ return new[] { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
}
}
@@ -224,13 +228,42 @@ namespace Emby.Drawing.Skia
var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path) ?? string.Empty);
+ fileSystem.CreateDirectory(fileSystem.GetDirectoryName(tempPath));
fileSystem.CopyFile(path, tempPath, true);
return tempPath;
}
+ private static SKCodecOrigin GetSKCodecOrigin(ImageOrientation? orientation)
+ {
+ if (!orientation.HasValue)
+ {
+ return SKCodecOrigin.TopLeft;
+ }
+
+ switch (orientation.Value)
+ {
+ case ImageOrientation.TopRight:
+ return SKCodecOrigin.TopRight;
+ case ImageOrientation.RightTop:
+ return SKCodecOrigin.RightTop;
+ case ImageOrientation.RightBottom:
+ return SKCodecOrigin.RightBottom;
+ case ImageOrientation.LeftTop:
+ return SKCodecOrigin.LeftTop;
+ case ImageOrientation.LeftBottom:
+ return SKCodecOrigin.LeftBottom;
+ case ImageOrientation.BottomRight:
+ return SKCodecOrigin.BottomRight;
+ case ImageOrientation.BottomLeft:
+ return SKCodecOrigin.BottomLeft;
+ default:
+ return SKCodecOrigin.TopLeft;
+ }
+ }
+
private static string[] TransparentImageTypes = new string[] { ".png", ".gif", ".webp" };
- internal static SKBitmap Decode(string path, bool forceCleanBitmap, IFileSystem fileSystem, out SKCodecOrigin origin)
+ internal static SKBitmap Decode(string path, bool forceCleanBitmap, IFileSystem fileSystem, ImageOrientation? orientation, out SKCodecOrigin origin)
{
if (!fileSystem.FileExists(path))
{
@@ -247,7 +280,7 @@ namespace Emby.Drawing.Skia
{
if (codec == null)
{
- origin = SKCodecOrigin.TopLeft;
+ origin = GetSKCodecOrigin(orientation);
return null;
}
@@ -258,12 +291,12 @@ namespace Emby.Drawing.Skia
{
// decode
codec.GetPixels(bitmap.Info, bitmap.GetPixels());
-
+
origin = codec.Origin;
}
else
{
- origin = SKCodecOrigin.TopLeft;
+ origin = GetSKCodecOrigin(orientation);
}
return bitmap;
@@ -275,7 +308,7 @@ namespace Emby.Drawing.Skia
if (resultBitmap == null)
{
- return Decode(path, true, fileSystem, out origin);
+ return Decode(path, true, fileSystem, orientation, out origin);
}
// If we have to resize these they often end up distorted
@@ -283,7 +316,7 @@ namespace Emby.Drawing.Skia
{
using (resultBitmap)
{
- return Decode(path, true, fileSystem, out origin);
+ return Decode(path, true, fileSystem, orientation, out origin);
}
}
@@ -291,26 +324,26 @@ namespace Emby.Drawing.Skia
return resultBitmap;
}
- private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, out SKCodecOrigin origin)
+ private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKCodecOrigin origin)
{
if (cropWhitespace)
{
- using (var bitmap = Decode(path, forceAnalyzeBitmap, _fileSystem, out origin))
+ using (var bitmap = Decode(path, forceAnalyzeBitmap, _fileSystem, orientation, out origin))
{
return CropWhiteSpace(bitmap);
}
}
- return Decode(path, forceAnalyzeBitmap, _fileSystem, out origin);
+ return Decode(path, forceAnalyzeBitmap, _fileSystem, orientation, out origin);
}
- private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient)
+ private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
{
SKCodecOrigin origin;
if (autoOrient)
{
- var bitmap = GetBitmap(path, cropWhitespace, true, out origin);
+ var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out origin);
if (bitmap != null)
{
@@ -326,7 +359,7 @@ namespace Emby.Drawing.Skia
return bitmap;
}
- return GetBitmap(path, cropWhitespace, false, out origin);
+ return GetBitmap(path, cropWhitespace, false, orientation, out origin);
}
private SKBitmap OrientImage(SKBitmap bitmap, SKCodecOrigin origin)
@@ -496,7 +529,7 @@ namespace Emby.Drawing.Skia
var blur = options.Blur ?? 0;
var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
- using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient))
+ using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation))
{
if (bitmap == null)
{
@@ -621,8 +654,7 @@ namespace Emby.Drawing.Skia
if (options.AddPlayedIndicator)
{
- var task = new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(canvas, currentImageSize);
- Task.WaitAll(task);
+ new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(canvas, currentImageSize);
}
else if (options.UnplayedCount.HasValue)
{
diff --git a/Emby.Drawing.Skia/StripCollageBuilder.cs b/Emby.Drawing.Skia/StripCollageBuilder.cs
index d562bb4be..85eeaa9f5 100644
--- a/Emby.Drawing.Skia/StripCollageBuilder.cs
+++ b/Emby.Drawing.Skia/StripCollageBuilder.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Common.Configuration;
using System;
using System.IO;
using MediaBrowser.Model.IO;
+using System.Collections.Generic;
namespace Emby.Drawing.Skia
{
@@ -82,9 +83,17 @@ namespace Emby.Drawing.Skia
for (int i = 0; i < 4; i++)
{
- SKCodecOrigin origin;
- using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, _fileSystem, out origin))
+ int newIndex;
+
+ using (var currentBitmap = GetNextValidImage(paths, imageIndex, out newIndex))
{
+ imageIndex = newIndex;
+
+ if (currentBitmap == null)
+ {
+ continue;
+ }
+
// resize to the same aspect as the original
int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
@@ -98,7 +107,12 @@ namespace Emby.Drawing.Skia
using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
{
// draw image onto canvas
- canvas.DrawImage(subset, (horizontalImagePadding * (i + 1)) + (iSlice * i), verticalSpacing);
+ canvas.DrawImage(subset ?? image, (horizontalImagePadding * (i + 1)) + (iSlice * i), verticalSpacing);
+
+ if (subset == null)
+ {
+ continue;
+ }
using (var croppedBitmap = SKBitmap.FromImage(subset))
{
@@ -140,14 +154,38 @@ namespace Emby.Drawing.Skia
}
}
}
+ }
+ }
+
+ return bitmap;
+ }
+
+ private SKBitmap GetNextValidImage(string[] paths, int currentIndex, out int newIndex)
+ {
+ Dictionary<int, int> imagesTested = new Dictionary<int, int>();
+ SKBitmap bitmap = null;
+
+ while (imagesTested.Count < paths.Length)
+ {
+ if (currentIndex >= paths.Length)
+ {
+ currentIndex = 0;
+ }
+
+ SKCodecOrigin origin;
+ bitmap = SkiaEncoder.Decode(paths[currentIndex], false, _fileSystem, null, out origin);
+
+ imagesTested[currentIndex] = 0;
- imageIndex++;
+ currentIndex++;
- if (imageIndex >= paths.Length)
- imageIndex = 0;
+ if (bitmap != null)
+ {
+ break;
}
}
+ newIndex = currentIndex;
return bitmap;
}
@@ -165,8 +203,17 @@ namespace Emby.Drawing.Skia
for (var y = 0; y < 2; y++)
{
SKCodecOrigin origin;
- using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, _fileSystem, out origin))
+ int newIndex;
+
+ using (var currentBitmap = GetNextValidImage(paths, imageIndex, out newIndex))
{
+ imageIndex = newIndex;
+
+ if (currentBitmap == null)
+ {
+ continue;
+ }
+
using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
{
// scale image
@@ -178,10 +225,6 @@ namespace Emby.Drawing.Skia
canvas.DrawBitmap(resizedBitmap, xPos, yPos);
}
}
- imageIndex++;
-
- if (imageIndex >= paths.Length)
- imageIndex = 0;
}
}
}
diff --git a/Emby.Drawing.Skia/packages.config b/Emby.Drawing.Skia/packages.config
deleted file mode 100644
index 2b9b0aee4..000000000
--- a/Emby.Drawing.Skia/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="SkiaSharp" version="1.58.1" targetFramework="portable45-net45+win8" />
-</packages> \ No newline at end of file
diff --git a/Emby.Drawing/Common/ImageHeader.cs b/Emby.Drawing/Common/ImageHeader.cs
index 4f56498a5..121e20deb 100644
--- a/Emby.Drawing/Common/ImageHeader.cs
+++ b/Emby.Drawing/Common/ImageHeader.cs
@@ -27,10 +27,10 @@ namespace Emby.Drawing.Common
/// The image format decoders
/// </summary>
private static readonly KeyValuePair<byte[], Func<BinaryReader, ImageSize>>[] ImageFormatDecoders = new Dictionary<byte[], Func<BinaryReader, ImageSize>>
- {
- { new byte[] { 0x42, 0x4D }, DecodeBitmap },
- { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
- { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
+ {
+ { new byte[] { 0x42, 0x4D }, DecodeBitmap },
+ { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
+ { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
{ new byte[] { 0xff, 0xd8 }, DecodeJfif }
@@ -38,6 +38,8 @@ namespace Emby.Drawing.Common
private static readonly int MaxMagicBytesLength = ImageFormatDecoders.Select(i => i.Key.Length).OrderByDescending(i => i).First();
+ private static string[] SupportedExtensions = new string[] { ".jpg", ".jpeg", ".png", ".gif" };
+
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
@@ -48,6 +50,17 @@ namespace Emby.Drawing.Common
/// <exception cref="ArgumentException">The image was of an unrecognised format.</exception>
public static ImageSize GetDimensions(string path, ILogger logger, IFileSystem fileSystem)
{
+ var extension = Path.GetExtension(path);
+
+ if (string.IsNullOrEmpty(extension))
+ {
+ throw new ArgumentException("ImageHeader doesn't support image file");
+ }
+ if (!SupportedExtensions.Contains(extension))
+ {
+ throw new ArgumentException("ImageHeader doesn't support " + extension);
+ }
+
using (var fs = fileSystem.OpenRead(path))
{
using (var binaryReader = new BinaryReader(fs))
diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj
index c5dd671a8..e4c9a0c35 100644
--- a/Emby.Drawing/Emby.Drawing.csproj
+++ b/Emby.Drawing/Emby.Drawing.csproj
@@ -1,67 +1,17 @@
-<?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>{08FFF49B-F175-4807-A2B5-73B0EBD9F716}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Drawing</RootNamespace>
- <AssemblyName>Emby.Drawing</AssemblyName>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- </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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Common\ImageHeader.cs" />
- <Compile Include="ImageProcessor.cs" />
- <Compile Include="NullImageEncoder.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</ItemGroup>
- <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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index f91cb4675..eeb5b8f9f 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -169,7 +169,7 @@ namespace Emby.Drawing
return _imageEncoder.SupportedOutputFormats;
}
- private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp" };
+ private readonly string[] TransparentImageTypes = new string[] { ".png", ".webp", ".gif" };
public bool SupportsTransparency(string path)
{
return TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
@@ -183,7 +183,7 @@ namespace Emby.Drawing
}
var originalImage = options.Image;
- IHasMetadata item = options.Item;
+ var item = options.Item;
if (!originalImage.IsLocalFile)
{
@@ -196,6 +196,7 @@ namespace Emby.Drawing
var originalImagePath = originalImage.Path;
var dateModified = originalImage.DateModified;
+ var originalImageSize = originalImage.Width > 0 && originalImage.Height > 0 ? new ImageSize(originalImage.Width, originalImage.Height) : (ImageSize?)null;
if (!_imageEncoder.SupportsImageEncoding)
{
@@ -207,7 +208,7 @@ namespace Emby.Drawing
dateModified = supportedImageInfo.Item2;
var requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath) ?? string.Empty);
- if (options.Enhancers.Count > 0)
+ if (options.Enhancers.Length > 0)
{
if (item == null)
{
@@ -225,18 +226,32 @@ namespace Emby.Drawing
originalImagePath = tuple.Item1;
dateModified = tuple.Item2;
requiresTransparency = tuple.Item3;
+ // TODO: Get this info
+ originalImageSize = null;
}
var photo = item as Photo;
var autoOrient = false;
ImageOrientation? orientation = null;
- if (photo != null && photo.Orientation.HasValue && photo.Orientation.Value != ImageOrientation.TopLeft)
+ if (photo != null)
{
- autoOrient = true;
- orientation = photo.Orientation;
+ if (photo.Orientation.HasValue)
+ {
+ if (photo.Orientation.Value != ImageOrientation.TopLeft)
+ {
+ autoOrient = true;
+ orientation = photo.Orientation;
+ }
+ }
+ else
+ {
+ // Orientation unknown, so do it
+ autoOrient = true;
+ orientation = photo.Orientation;
+ }
}
- if (options.HasDefaultOptions(originalImagePath) && (!autoOrient || !options.RequiresAutoOrientation))
+ if (options.HasDefaultOptions(originalImagePath, originalImageSize) && (!autoOrient || !options.RequiresAutoOrientation))
{
// Just spit out the original file if all the options are default
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
@@ -247,7 +262,7 @@ namespace Emby.Drawing
//{
// // Just spit out the original file if all the options are default
// _logger.Info("Returning original image {0}", originalImagePath);
- // return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+ // return new ValueTuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
//}
var newSize = ImageHelper.GetNewImageSize(options, null);
@@ -317,7 +332,7 @@ namespace Emby.Drawing
}
// If transparency is needed and webp isn't supported, than png is the only option
- if (requiresTransparency)
+ if (requiresTransparency && clientSupportedFormats.Contains(ImageFormat.Png))
{
return ImageFormat.Png;
}
@@ -346,21 +361,6 @@ namespace Emby.Drawing
}
}
- //private static int[][] OPERATIONS = new int[][] {
- // TopLeft
- //new int[] { 0, NONE},
- // TopRight
- //new int[] { 0, HORIZONTAL},
- //new int[] {180, NONE},
- // LeftTop
- //new int[] { 0, VERTICAL},
- //new int[] { 90, HORIZONTAL},
- // RightTop
- //new int[] { 90, NONE},
- //new int[] {-90, HORIZONTAL},
- //new int[] {-90, NONE},
- //};
-
private string GetMimeType(ImageFormat format, string path)
{
if (format == ImageFormat.Bmp)
@@ -494,55 +494,19 @@ namespace Emby.Drawing
throw new ArgumentNullException("path");
}
- return GetImageSizeInternal(path, allowSlowMethod);
- }
-
- /// <summary>
- /// Gets the image size internal.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="allowSlowMethod">if set to <c>true</c> [allow slow method].</param>
- /// <returns>ImageSize.</returns>
- private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
- {
- //try
- //{
- // using (var fileStream = _fileSystem.OpenRead(path))
- // {
- // using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), fileStream, null)))
- // {
- // var image = file as TagLib.Image.File;
-
- // if (image != null)
- // {
- // var properties = image.Properties;
-
- // return new ImageSize
- // {
- // Height = properties.PhotoHeight,
- // Width = properties.PhotoWidth
- // };
- // }
- // }
- // }
- //}
- //catch
- //{
- //}
-
try
{
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
}
catch
{
- if (allowSlowMethod)
+ if (!allowSlowMethod)
{
- return _imageEncoder.GetImageSize(path);
+ throw;
}
-
- throw;
}
+
+ return _imageEncoder.GetImageSize(path);
}
/// <summary>
@@ -552,21 +516,28 @@ namespace Emby.Drawing
/// <param name="image">The image.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public string GetImageCacheTag(IHasMetadata item, ItemImageInfo image)
+ public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
{
- if (item == null)
+ var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
+
+ return GetImageCacheTag(item, image, supportedEnhancers);
+ }
+
+ public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
+ {
+ try
{
- throw new ArgumentNullException("item");
+ return GetImageCacheTag(item, new ItemImageInfo
+ {
+ Path = chapter.ImagePath,
+ Type = ImageType.Chapter,
+ DateModified = chapter.ImageDateModified
+ });
}
-
- if (image == null)
+ catch
{
- throw new ArgumentNullException("image");
+ return null;
}
-
- var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
-
- return GetImageCacheTag(item, image, supportedEnhancers);
}
/// <summary>
@@ -577,29 +548,14 @@ namespace Emby.Drawing
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public string GetImageCacheTag(IHasMetadata item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers)
+ public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers)
{
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- if (imageEnhancers == null)
- {
- throw new ArgumentNullException("imageEnhancers");
- }
-
- if (image == null)
- {
- throw new ArgumentNullException("image");
- }
-
var originalImagePath = image.Path;
var dateModified = image.DateModified;
var imageType = image.Type;
// Optimization
- if (imageEnhancers.Count == 0)
+ if (imageEnhancers.Length == 0)
{
return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N");
}
@@ -611,7 +567,7 @@ namespace Emby.Drawing
return string.Join("|", cacheKeys.ToArray(cacheKeys.Count)).GetMD5().ToString("N");
}
- private async Task<Tuple<string, DateTime>> GetSupportedImage(string originalImagePath, DateTime dateModified)
+ private async Task<ValueTuple<string, DateTime>> GetSupportedImage(string originalImagePath, DateTime dateModified)
{
var inputFormat = (Path.GetExtension(originalImagePath) ?? string.Empty)
.TrimStart('.')
@@ -620,7 +576,7 @@ namespace Emby.Drawing
// These are just jpg files renamed as tbn
if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, DateTime>(originalImagePath, dateModified);
+ return new ValueTuple<string, DateTime>(originalImagePath, dateModified);
}
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
@@ -651,7 +607,7 @@ namespace Emby.Drawing
}
}
- return new Tuple<string, DateTime>(originalImagePath, dateModified);
+ return new ValueTuple<string, DateTime>(originalImagePath, dateModified);
}
/// <summary>
@@ -661,7 +617,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(IHasMetadata item, ImageType imageType, int imageIndex)
+ public async Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex)
{
var enhancers = GetSupportedEnhancers(item, imageType);
@@ -674,11 +630,11 @@ namespace Emby.Drawing
return result.Item1;
}
- private async Task<Tuple<string, DateTime, bool>> GetEnhancedImage(ItemImageInfo image,
+ private async Task<ValueTuple<string, DateTime, bool>> GetEnhancedImage(ItemImageInfo image,
bool inputImageSupportsTransparency,
- IHasMetadata item,
+ BaseItem item,
int imageIndex,
- List<IImageEnhancer> enhancers,
+ IImageEnhancer[] enhancers,
CancellationToken cancellationToken)
{
var originalImagePath = image.Path;
@@ -699,7 +655,7 @@ namespace Emby.Drawing
{
var treatmentRequiresTransparency = ehnancedImageInfo.Item2;
- return new Tuple<string, DateTime, bool>(ehnancedImagePath, _fileSystem.GetLastWriteTimeUtc(ehnancedImagePath), treatmentRequiresTransparency);
+ return new ValueTuple<string, DateTime, bool>(ehnancedImagePath, _fileSystem.GetLastWriteTimeUtc(ehnancedImagePath), treatmentRequiresTransparency);
}
}
catch (Exception ex)
@@ -707,7 +663,7 @@ namespace Emby.Drawing
_logger.ErrorException("Error enhancing image", ex);
}
- return new Tuple<string, DateTime, bool>(originalImagePath, dateModified, inputImageSupportsTransparency);
+ return new ValueTuple<string, DateTime, bool>(originalImagePath, dateModified, inputImageSupportsTransparency);
}
/// <summary>
@@ -725,11 +681,11 @@ namespace Emby.Drawing
/// or
/// item
/// </exception>
- private async Task<Tuple<string, bool>> GetEnhancedImageInternal(string originalImagePath,
- IHasMetadata item,
+ private async Task<ValueTuple<string, bool>> GetEnhancedImageInternal(string originalImagePath,
+ BaseItem item,
ImageType imageType,
int imageIndex,
- List<IImageEnhancer> supportedEnhancers,
+ IImageEnhancer[] supportedEnhancers,
string cacheGuid,
CancellationToken cancellationToken)
{
@@ -753,8 +709,8 @@ namespace Emby.Drawing
}
// All enhanced images are saved as png to allow transparency
- var cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ?
- ".webp" :
+ var cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ?
+ ".webp" :
(treatmentRequiresTransparency ? ".png" : ".jpg");
var enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension);
@@ -768,14 +724,14 @@ namespace Emby.Drawing
// Check again in case of contention
if (_fileSystem.FileExists(enhancedImagePath))
{
- return new Tuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
+ return new ValueTuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
}
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
- return new Tuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
+ return new ValueTuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
}
finally
{
@@ -793,7 +749,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, IHasMetadata item, ImageType imageType, int imageIndex)
+ private async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex)
{
// Run the enhancers sequentially in order of priority
foreach (var enhancer in imageEnhancers)
@@ -878,9 +834,9 @@ namespace Emby.Drawing
_logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
}
- public List<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType)
+ public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType)
{
- var list = new List<IImageEnhancer>();
+ List<IImageEnhancer> list = null;
foreach (var i in ImageEnhancers)
{
@@ -888,6 +844,10 @@ namespace Emby.Drawing
{
if (i.Supports(item, imageType))
{
+ if (list == null)
+ {
+ list = new List<IImageEnhancer>();
+ }
list.Add(i);
}
}
@@ -896,7 +856,8 @@ namespace Emby.Drawing
_logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name);
}
}
- return list;
+
+ return list == null ? Array.Empty<IImageEnhancer>() : list.ToArray();
}
private Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
@@ -948,8 +909,6 @@ namespace Emby.Drawing
{
disposable.Dispose();
}
-
- GC.SuppressFinalize(this);
}
private void CheckDisposed()
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
new file mode 100644
index 000000000..c88631a0c
--- /dev/null
+++ b/Emby.Naming/Audio/AlbumParser.cs
@@ -0,0 +1,65 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Audio
+{
+ public class AlbumParser
+ {
+ private readonly NamingOptions _options;
+
+ public AlbumParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public MultiPartResult ParseMultiPart(string path)
+ {
+ var result = new MultiPartResult();
+
+ var filename = Path.GetFileName(path);
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ return result;
+ }
+
+ // TODO: Move this logic into options object
+ // Even better, remove the prefixes and come up with regexes
+ // But Kodi documentation seems to be weak for audio
+
+ // Normalize
+ // Remove whitespace
+ filename = filename.Replace("-", " ");
+ filename = filename.Replace(".", " ");
+ filename = filename.Replace("(", " ");
+ filename = filename.Replace(")", " ");
+ filename = Regex.Replace(filename, @"\s+", " ");
+
+ filename = filename.TrimStart();
+
+ foreach (var prefix in _options.AlbumStackingPrefixes)
+ {
+ if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ var tmp = filename.Substring(prefix.Length);
+
+ tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
+
+ int val;
+ if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+ {
+ result.IsMultiPart = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs
new file mode 100644
index 000000000..20016915a
--- /dev/null
+++ b/Emby.Naming/Audio/AudioFileParser.cs
@@ -0,0 +1,23 @@
+using System;
+using System.IO;
+using System.Linq;
+using Emby.Naming.Common;
+
+namespace Emby.Naming.Audio
+{
+ public class AudioFileParser
+ {
+ private readonly NamingOptions _options;
+
+ public AudioFileParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public bool IsAudioFile(string path)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ return _options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs
new file mode 100644
index 000000000..fae0ae4d8
--- /dev/null
+++ b/Emby.Naming/Audio/MultiPartResult.cs
@@ -0,0 +1,22 @@
+
+namespace Emby.Naming.Audio
+{
+ public class MultiPartResult
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the part.
+ /// </summary>
+ /// <value>The part.</value>
+ public string Part { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is multi part.
+ /// </summary>
+ /// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
+ public bool IsMultiPart { get; set; }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs
new file mode 100644
index 000000000..88a98d0f1
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs
@@ -0,0 +1,49 @@
+
+using System;
+using Emby.Naming.Video;
+
+namespace Emby.Naming.AudioBook
+{
+ /// <summary>
+ /// Represents a single video file
+ /// </summary>
+ public class AudioBookFileInfo : IComparable<AudioBookFileInfo>
+ {
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ public string Path { get; set; }
+ /// <summary>
+ /// Gets or sets the container.
+ /// </summary>
+ /// <value>The container.</value>
+ public string Container { get; set; }
+ /// <summary>
+ /// Gets or sets the part number.
+ /// </summary>
+ /// <value>The part number.</value>
+ public int? PartNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the chapter number.
+ /// </summary>
+ /// <value>The chapter number.</value>
+ public int? ChapterNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ /// <value>The type.</value>
+ public bool IsDirectory { get; set; }
+
+ public int CompareTo(AudioBookFileInfo other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+ var chapterNumberComparison = Nullable.Compare(ChapterNumber, other.ChapterNumber);
+ if (chapterNumberComparison != 0) return chapterNumberComparison;
+ var partNumberComparison = Nullable.Compare(PartNumber, other.PartNumber);
+ if (partNumberComparison != 0) return partNumberComparison;
+ return string.Compare(Path, other.Path, StringComparison.Ordinal);
+ }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
new file mode 100644
index 000000000..b4805edd2
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+
+namespace Emby.Naming.AudioBook
+{
+ public class AudioBookFilePathParser
+ {
+ private readonly NamingOptions _options;
+
+ public AudioBookFilePathParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public AudioBookFilePathParserResult Parse(string path, bool IsDirectory)
+ {
+ AudioBookFilePathParserResult result = Parse(path);
+ return !result.Success ? new AudioBookFilePathParserResult() : result;
+ }
+
+ private AudioBookFilePathParserResult Parse(string path)
+ {
+ var result = new AudioBookFilePathParserResult();
+ var fileName = Path.GetFileNameWithoutExtension(path);
+ foreach (var expression in _options.AudioBookPartsExpressions)
+ {
+ var match = new Regex(expression, RegexOptions.IgnoreCase).Match(fileName);
+ if (match.Success)
+ {
+ if (!result.ChapterNumber.HasValue)
+ {
+ var value = match.Groups["chapter"];
+ if (value.Success)
+ {
+ int intValue;
+ if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out intValue))
+ {
+ result.ChapterNumber = intValue;
+ }
+ }
+ }
+ if (!result.PartNumber.HasValue)
+ {
+ var value = match.Groups["part"];
+ if (value.Success)
+ {
+ int intValue;
+ if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out intValue))
+ {
+ result.ChapterNumber = intValue;
+ }
+ }
+ }
+ }
+ }
+
+ /*var matches = _iRegexProvider.GetRegex("\\d+", RegexOptions.IgnoreCase).Matches(fileName);
+ if (matches.Count > 0)
+ {
+ if (!result.ChapterNumber.HasValue)
+ {
+ result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
+ }
+ if (matches.Count > 1)
+ {
+ result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);
+ }
+ }*/
+ result.Success = result.PartNumber.HasValue || result.ChapterNumber.HasValue;
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs
new file mode 100644
index 000000000..759e7b8ad
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Emby.Naming.AudioBook
+{
+ public class AudioBookFilePathParserResult
+ {
+ public int? PartNumber { get; set; }
+ public int? ChapterNumber { get; set; }
+ public bool Success { get; set; }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs
new file mode 100644
index 000000000..e039e5359
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookInfo.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using Emby.Naming.Video;
+
+namespace Emby.Naming.AudioBook
+{
+ /// <summary>
+ /// Represents a complete video, including all parts and subtitles
+ /// </summary>
+ public class AudioBookInfo
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ public int? Year { get; set; }
+ /// <summary>
+ /// Gets or sets the files.
+ /// </summary>
+ /// <value>The files.</value>
+ public List<AudioBookFileInfo> Files { get; set; }
+ /// <summary>
+ /// Gets or sets the extras.
+ /// </summary>
+ /// <value>The extras.</value>
+ public List<AudioBookFileInfo> Extras { get; set; }
+ /// <summary>
+ /// Gets or sets the alternate versions.
+ /// </summary>
+ /// <value>The alternate versions.</value>
+ public List<AudioBookFileInfo> AlternateVersions { get; set; }
+
+ public AudioBookInfo()
+ {
+ Files = new List<AudioBookFileInfo>();
+ Extras = new List<AudioBookFileInfo>();
+ AlternateVersions = new List<AudioBookFileInfo>();
+ }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
new file mode 100644
index 000000000..8cf6a03bd
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Naming.AudioBook
+{
+ public class AudioBookListResolver
+ {
+ private readonly NamingOptions _options;
+
+ public AudioBookListResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public IEnumerable<AudioBookInfo> Resolve(List<FileSystemMetadata> files)
+ {
+ var audioBookResolver = new AudioBookResolver(_options);
+
+ var audiobookFileInfos = files
+ .Select(i => audioBookResolver.Resolve(i.FullName, i.IsDirectory))
+ .Where(i => i != null)
+ .ToList();
+
+ // Filter out all extras, otherwise they could cause stacks to not be resolved
+ // See the unit test TestStackedWithTrailer
+ var metadata = audiobookFileInfos
+ .Select(i => new FileSystemMetadata
+ {
+ FullName = i.Path,
+ IsDirectory = i.IsDirectory
+ });
+
+ var stackResult = new StackResolver(_options)
+ .ResolveAudioBooks(metadata);
+
+ var list = new List<AudioBookInfo>();
+
+ foreach (var stack in stackResult.Stacks)
+ {
+ var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList();
+ stackFiles.Sort();
+ var info = new AudioBookInfo
+ {
+ Files = stackFiles,
+ Name = stack.Name
+ };
+ list.Add(info);
+ }
+
+ // Whatever files are left, just add them
+ /*list.AddRange(remainingFiles.Select(i => new AudioBookInfo
+ {
+ Files = new List<AudioBookFileInfo> { i },
+ Name = i.,
+ Year = i.Year
+ }));*/
+
+ var orderedList = list.OrderBy(i => i.Name);
+
+ return orderedList;
+ }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs
new file mode 100644
index 000000000..a206ee30b
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookResolver.cs
@@ -0,0 +1,60 @@
+using System;
+using System.IO;
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Emby.Naming.Video;
+
+namespace Emby.Naming.AudioBook
+{
+ public class AudioBookResolver
+ {
+ private readonly NamingOptions _options;
+
+ public AudioBookResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public AudioBookFileInfo ParseFile(string path)
+ {
+ return Resolve(path, false);
+ }
+
+ public AudioBookFileInfo ParseDirectory(string path)
+ {
+ return Resolve(path, true);
+ }
+
+ public AudioBookFileInfo Resolve(string path, bool IsDirectory = false)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+ if (IsDirectory)
+ return null;
+
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ // Check supported extensions
+ if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var container = extension.TrimStart('.');
+
+ var parsingResult = new AudioBookFilePathParser(_options)
+ .Parse(path, IsDirectory);
+
+ return new AudioBookFileInfo
+ {
+ Path = path,
+ Container = container,
+ PartNumber = parsingResult.PartNumber,
+ ChapterNumber = parsingResult.ChapterNumber,
+ IsDirectory = IsDirectory
+ };
+ }
+ }
+}
diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs
new file mode 100644
index 000000000..3b9687c60
--- /dev/null
+++ b/Emby.Naming/Common/EpisodeExpression.cs
@@ -0,0 +1,45 @@
+using System.Text.RegularExpressions;
+using System;
+
+namespace Emby.Naming.Common
+{
+ public class EpisodeExpression
+ {
+ private string _expression;
+ public string Expression { get { return _expression; } set { _expression = value; _regex = null; } }
+
+ public bool IsByDate { get; set; }
+ public bool IsOptimistic { get; set; }
+ public bool IsNamed { get; set; }
+ public bool SupportsAbsoluteEpisodeNumbers { get; set; }
+
+ public string[] DateTimeFormats { get; set; }
+
+ private Regex _regex;
+ public Regex Regex
+ {
+ get
+ {
+ return _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
+ }
+ }
+
+ public EpisodeExpression(string expression, bool byDate)
+ {
+ Expression = expression;
+ IsByDate = byDate;
+ DateTimeFormats = Array.Empty<string>();
+ SupportsAbsoluteEpisodeNumbers = true;
+ }
+
+ public EpisodeExpression(string expression)
+ : this(expression, false)
+ {
+ }
+
+ public EpisodeExpression()
+ : this(null)
+ {
+ }
+ }
+}
diff --git a/Emby.Naming/Common/MediaType.cs b/Emby.Naming/Common/MediaType.cs
new file mode 100644
index 000000000..2a3d433cf
--- /dev/null
+++ b/Emby.Naming/Common/MediaType.cs
@@ -0,0 +1,19 @@
+
+namespace Emby.Naming.Common
+{
+ public enum MediaType
+ {
+ /// <summary>
+ /// The audio
+ /// </summary>
+ Audio = 0,
+ /// <summary>
+ /// The photo
+ /// </summary>
+ Photo = 1,
+ /// <summary>
+ /// The video
+ /// </summary>
+ Video = 2
+ }
+}
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
new file mode 100644
index 000000000..9e65440d0
--- /dev/null
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -0,0 +1,687 @@
+using Emby.Naming.Video;
+using System.Collections.Generic;
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Common
+{
+ public class NamingOptions
+ {
+ public string[] AudioFileExtensions { get; set; }
+ public string[] AlbumStackingPrefixes { get; set; }
+
+ public string[] SubtitleFileExtensions { get; set; }
+ public char[] SubtitleFlagDelimiters { get; set; }
+
+ public string[] SubtitleForcedFlags { get; set; }
+ public string[] SubtitleDefaultFlags { get; set; }
+
+ public EpisodeExpression[] EpisodeExpressions { get; set; }
+ public string[] EpisodeWithoutSeasonExpressions { get; set; }
+ public string[] EpisodeMultiPartExpressions { get; set; }
+
+ public string[] VideoFileExtensions { get; set; }
+ public string[] StubFileExtensions { get; set; }
+
+ public string[] AudioBookPartsExpressions { get; set; }
+
+ public StubTypeRule[] StubTypes { get; set; }
+
+ public char[] VideoFlagDelimiters { get; set; }
+ public Format3DRule[] Format3DRules { get; set; }
+
+ public string[] VideoFileStackingExpressions { get; set; }
+ public string[] CleanDateTimes { get; set; }
+ public string[] CleanStrings { get; set; }
+
+
+ public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
+
+ public ExtraRule[] VideoExtraRules { get; set; }
+
+ public NamingOptions()
+ {
+ VideoFileExtensions = new string[]
+ {
+ ".m4v",
+ ".3gp",
+ ".nsv",
+ ".ts",
+ ".ty",
+ ".strm",
+ ".rm",
+ ".rmvb",
+ ".ifo",
+ ".mov",
+ ".qt",
+ ".divx",
+ ".xvid",
+ ".bivx",
+ ".vob",
+ ".nrg",
+ ".img",
+ ".iso",
+ ".pva",
+ ".wmv",
+ ".asf",
+ ".asx",
+ ".ogm",
+ ".m2v",
+ ".avi",
+ ".bin",
+ ".dvr-ms",
+ ".mpg",
+ ".mpeg",
+ ".mp4",
+ ".mkv",
+ ".avc",
+ ".vp3",
+ ".svq3",
+ ".nuv",
+ ".viv",
+ ".dv",
+ ".fli",
+ ".flv",
+ ".001",
+ ".tp"
+ };
+
+ VideoFlagDelimiters = new[]
+ {
+ '(',
+ ')',
+ '-',
+ '.',
+ '_',
+ '[',
+ ']'
+ };
+
+ StubFileExtensions = new[]
+ {
+ ".disc"
+ };
+
+ StubTypes = new[]
+ {
+ new StubTypeRule
+ {
+ StubType = "dvd",
+ Token = "dvd"
+ },
+ new StubTypeRule
+ {
+ StubType = "hddvd",
+ Token = "hddvd"
+ },
+ new StubTypeRule
+ {
+ StubType = "bluray",
+ Token = "bluray"
+ },
+ new StubTypeRule
+ {
+ StubType = "bluray",
+ Token = "brrip"
+ },
+ new StubTypeRule
+ {
+ StubType = "bluray",
+ Token = "bd25"
+ },
+ new StubTypeRule
+ {
+ StubType = "bluray",
+ Token = "bd50"
+ },
+ new StubTypeRule
+ {
+ StubType = "vhs",
+ Token = "vhs"
+ },
+ new StubTypeRule
+ {
+ StubType = "tv",
+ Token = "HDTV"
+ },
+ new StubTypeRule
+ {
+ StubType = "tv",
+ Token = "PDTV"
+ },
+ new StubTypeRule
+ {
+ StubType = "tv",
+ Token = "DSR"
+ }
+ };
+
+ VideoFileStackingExpressions = new[]
+ {
+ "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$",
+ "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$",
+ "(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$"
+ };
+
+ CleanDateTimes = new[]
+ {
+ @"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)"
+ };
+
+ CleanStrings = new[]
+ {
+ @"[ _\,\.\(\)\[\]\-](ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"(\[.*\])"
+ };
+
+ SubtitleFileExtensions = new[]
+ {
+ ".srt",
+ ".ssa",
+ ".ass",
+ ".sub"
+ };
+
+ SubtitleFlagDelimiters = new[]
+ {
+ '.'
+ };
+
+ SubtitleForcedFlags = new[]
+ {
+ "foreign",
+ "forced"
+ };
+
+ SubtitleDefaultFlags = new[]
+ {
+ "default"
+ };
+
+ AlbumStackingPrefixes = new[]
+ {
+ "disc",
+ "cd",
+ "disk",
+ "vol",
+ "volume"
+ };
+
+ AudioFileExtensions = new[]
+ {
+ ".nsv",
+ ".m4a",
+ ".flac",
+ ".aac",
+ ".strm",
+ ".pls",
+ ".rm",
+ ".mpa",
+ ".wav",
+ ".wma",
+ ".ogg",
+ ".opus",
+ ".mp3",
+ ".mp2",
+ ".mod",
+ ".amf",
+ ".669",
+ ".dmf",
+ ".dsm",
+ ".far",
+ ".gdm",
+ ".imf",
+ ".it",
+ ".m15",
+ ".med",
+ ".okt",
+ ".s3m",
+ ".stm",
+ ".sfx",
+ ".ult",
+ ".uni",
+ ".xm",
+ ".sid",
+ ".ac3",
+ ".dts",
+ ".cue",
+ ".aif",
+ ".aiff",
+ ".ape",
+ ".mac",
+ ".mpc",
+ ".mp+",
+ ".mpp",
+ ".shn",
+ ".wv",
+ ".nsf",
+ ".spc",
+ ".gym",
+ ".adplug",
+ ".adx",
+ ".dsp",
+ ".adp",
+ ".ymf",
+ ".ast",
+ ".afc",
+ ".hps",
+ ".xsp",
+ ".acc",
+ ".m4b",
+ ".oga",
+ ".dsf",
+ ".mka"
+ };
+
+ EpisodeExpressions = new[]
+ {
+ // *** Begin Kodi Standard Naming
+ // <!-- foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02 -->
+ new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+))[^\\\/])*)?[Ss](?<seasonnumber>[0-9]+)[][ ._-]*[Ee](?<epnumber>[0-9]+)([^\\/]*)$")
+ {
+ IsNamed = true
+ },
+ // <!-- foo.ep01, foo.EP_01 -->
+ new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
+ new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true)
+ {
+ DateTimeFormats = new []
+ {
+ "yyyy.MM.dd",
+ "yyyy-MM-dd",
+ "yyyy_MM_dd"
+ }
+ },
+ new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true)
+ {
+ DateTimeFormats = new []
+ {
+ "dd.MM.yyyy",
+ "dd-MM-yyyy",
+ "dd_MM_yyyy"
+ }
+ },
+
+ new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$")
+ {
+ SupportsAbsoluteEpisodeNumbers = true
+ },
+ new EpisodeExpression(@"[\\\\/\\._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/])*)[\\\\/\\._ -](?<seasonnumber>[0-9]+)(?<epnumber>[0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$")
+ {
+ IsOptimistic = true,
+ IsNamed = true,
+ SupportsAbsoluteEpisodeNumbers = false
+ },
+ new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$")
+ {
+ SupportsAbsoluteEpisodeNumbers = true
+ },
+
+ // *** End Kodi Standard Naming
+
+ new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ // "01.avi"
+ new EpisodeExpression(@".*[\\\/](?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.\w+$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+
+ // "1-12 episode title"
+ new EpisodeExpression(@"([0-9]+)-([0-9]+)")
+ {
+ },
+
+ // "01 - blah.avi", "01-blah.avi"
+ new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+
+ // "01.blah.avi"
+ new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.[^\\\/]+$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+
+ // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah"
+ new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+
+ // "01 episode title.avi"
+ new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>\d{1,3})([^\\\/]*)$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+ // "Episode 16", "Episode 16 - Title"
+ new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ }
+ };
+
+ EpisodeWithoutSeasonExpressions = new[]
+ {
+ @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
+ };
+
+ EpisodeMultiPartExpressions = new[]
+ {
+ @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
+ };
+
+ VideoExtraRules = new[]
+ {
+ new ExtraRule
+ {
+ ExtraType = "trailer",
+ RuleType = ExtraRuleType.Filename,
+ Token = "trailer",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "trailer",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-trailer",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "trailer",
+ RuleType = ExtraRuleType.Suffix,
+ Token = ".trailer",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "trailer",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "_trailer",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "trailer",
+ RuleType = ExtraRuleType.Suffix,
+ Token = " trailer",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "sample",
+ RuleType = ExtraRuleType.Filename,
+ Token = "sample",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "sample",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-sample",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "sample",
+ RuleType = ExtraRuleType.Suffix,
+ Token = ".sample",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "sample",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "_sample",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "sample",
+ RuleType = ExtraRuleType.Suffix,
+ Token = " sample",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "themesong",
+ RuleType = ExtraRuleType.Filename,
+ Token = "theme",
+ MediaType = MediaType.Audio
+ },
+
+ new ExtraRule
+ {
+ ExtraType = "scene",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-scene",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "clip",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-clip",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "interview",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-interview",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "behindthescenes",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-behindthescenes",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "deletedscene",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-deleted",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "featurette",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-featurette",
+ MediaType = MediaType.Video
+ },
+ new ExtraRule
+ {
+ ExtraType = "short",
+ RuleType = ExtraRuleType.Suffix,
+ Token = "-short",
+ MediaType = MediaType.Video
+ }
+
+ };
+ Format3DRules = new[]
+ {
+ // Kodi rules:
+ new Format3DRule
+ {
+ PreceedingToken = "3d",
+ Token = "hsbs"
+ },
+ new Format3DRule
+ {
+ PreceedingToken = "3d",
+ Token = "sbs"
+ },
+ new Format3DRule
+ {
+ PreceedingToken = "3d",
+ Token = "htab"
+ },
+ new Format3DRule
+ {
+ PreceedingToken = "3d",
+ Token = "tab"
+ },
+ // Media Browser rules:
+ new Format3DRule
+ {
+ Token = "fsbs"
+ },
+ new Format3DRule
+ {
+ Token = "hsbs"
+ },
+ new Format3DRule
+ {
+ Token = "sbs"
+ },
+ new Format3DRule
+ {
+ Token = "ftab"
+ },
+ new Format3DRule
+ {
+ Token = "htab"
+ },
+ new Format3DRule
+ {
+ Token = "tab"
+ },
+ new Format3DRule
+ {
+ Token = "sbs3d"
+ },
+ new Format3DRule
+ {
+ Token = "mvc"
+ }
+ };
+ AudioBookPartsExpressions = new[]
+ {
+ // Detect specified chapters, like CH 01
+ @"ch(?:apter)?[\s_-]?(?<chapter>\d+)",
+ // Detect specified parts, like Part 02
+ @"p(?:ar)?t[\s_-]?(?<part>\d+)",
+ // Chapter is often beginning of filename
+ @"^(?<chapter>\d+)",
+ // Part if often ending of filename
+ @"(?<part>\d+)$",
+ // Sometimes named as 0001_005 (chapter_part)
+ @"(?<chapter>\d+)_(?<part>\d+)",
+ // Some audiobooks are ripped from cd's, and will be named by disk number.
+ @"dis(?:c|k)[\s_-]?(?<chapter>\d+)"
+ };
+
+ var extensions = VideoFileExtensions.ToList();
+
+ extensions.AddRange(new[]
+ {
+ ".mkv",
+ ".m2t",
+ ".m2ts",
+ ".img",
+ ".iso",
+ ".mk3d",
+ ".ts",
+ ".rmvb",
+ ".mov",
+ ".avi",
+ ".mpg",
+ ".mpeg",
+ ".wmv",
+ ".mp4",
+ ".divx",
+ ".dvr-ms",
+ ".wtv",
+ ".ogm",
+ ".ogv",
+ ".asf",
+ ".m4v",
+ ".flv",
+ ".f4v",
+ ".3gp",
+ ".webm",
+ ".mts",
+ ".m2v",
+ ".rec",
+ ".mxf"
+ });
+
+ MultipleEpisodeExpressions = new string[]
+ {
+ @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
+ @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$"
+
+ }.Select(i => new EpisodeExpression(i)
+ {
+ IsNamed = true
+
+ }).ToArray();
+
+ VideoFileExtensions = extensions
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ Compile();
+ }
+
+ public Regex[] VideoFileStackingRegexes { get; private set; }
+ public Regex[] CleanDateTimeRegexes { get; private set; }
+ public Regex[] CleanStringRegexes { get; private set; }
+
+ public Regex[] EpisodeWithoutSeasonRegexes { get; private set; }
+ public Regex[] EpisodeMultiPartRegexes { get; private set; }
+
+ public void Compile()
+ {
+ VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
+ CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
+ CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
+ EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
+ EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
+ }
+
+ private Regex Compile(string exp)
+ {
+ return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ }
+ }
+}
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
new file mode 100644
index 000000000..89562296d
--- /dev/null
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/Emby.Naming/Extensions/StringExtensions.cs b/Emby.Naming/Extensions/StringExtensions.cs
new file mode 100644
index 000000000..e773dbfe8
--- /dev/null
+++ b/Emby.Naming/Extensions/StringExtensions.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Text;
+
+namespace Emby.Naming.Extensions
+{
+ public static class StringExtensions
+ {
+ public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
+ {
+ var sb = new StringBuilder();
+
+ var previousIndex = 0;
+ var index = str.IndexOf(oldValue, comparison);
+
+ while (index != -1)
+ {
+ sb.Append(str.Substring(previousIndex, index - previousIndex));
+ sb.Append(newValue);
+ index += oldValue.Length;
+
+ previousIndex = index;
+ index = str.IndexOf(oldValue, index, comparison);
+ }
+
+ sb.Append(str.Substring(previousIndex));
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Emby.Naming/StringExtensions.cs b/Emby.Naming/StringExtensions.cs
new file mode 100644
index 000000000..5598ddb4c
--- /dev/null
+++ b/Emby.Naming/StringExtensions.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Emby.Naming
+{
+ internal static class StringExtensions
+ {
+ public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
+ {
+ var sb = new StringBuilder();
+
+ var previousIndex = 0;
+ var index = str.IndexOf(oldValue, comparison);
+
+ while (index != -1)
+ {
+ sb.Append(str.Substring(previousIndex, index - previousIndex));
+ sb.Append(newValue);
+ index += oldValue.Length;
+
+ previousIndex = index;
+ index = str.IndexOf(oldValue, index, comparison);
+ }
+
+ sb.Append(str.Substring(previousIndex));
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Emby.Naming/Subtitles/SubtitleInfo.cs b/Emby.Naming/Subtitles/SubtitleInfo.cs
new file mode 100644
index 000000000..3ece0679e
--- /dev/null
+++ b/Emby.Naming/Subtitles/SubtitleInfo.cs
@@ -0,0 +1,27 @@
+
+namespace Emby.Naming.Subtitles
+{
+ public class SubtitleInfo
+ {
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ public string Path { get; set; }
+ /// <summary>
+ /// Gets or sets the language.
+ /// </summary>
+ /// <value>The language.</value>
+ public string Language { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is default.
+ /// </summary>
+ /// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
+ public bool IsDefault { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is forced.
+ /// </summary>
+ /// <value><c>true</c> if this instance is forced; otherwise, <c>false</c>.</value>
+ public bool IsForced { get; set; }
+ }
+}
diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs
new file mode 100644
index 000000000..fbf0f60cb
--- /dev/null
+++ b/Emby.Naming/Subtitles/SubtitleParser.cs
@@ -0,0 +1,65 @@
+using Emby.Naming.Common;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Emby.Naming.Subtitles
+{
+ public class SubtitleParser
+ {
+ private readonly NamingOptions _options;
+
+ public SubtitleParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public SubtitleInfo ParseFile(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var extension = Path.GetExtension(path);
+ if (!_options.SubtitleFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var flags = GetFlags(path);
+
+ var info = new SubtitleInfo
+ {
+ Path = path,
+ IsDefault = _options.SubtitleDefaultFlags.Any(i => flags.Contains(i, StringComparer.OrdinalIgnoreCase)),
+ IsForced = _options.SubtitleForcedFlags.Any(i => flags.Contains(i, StringComparer.OrdinalIgnoreCase))
+ };
+
+ var parts = flags.Where(i => !_options.SubtitleDefaultFlags.Contains(i, StringComparer.OrdinalIgnoreCase) && !_options.SubtitleForcedFlags.Contains(i, StringComparer.OrdinalIgnoreCase))
+ .ToList();
+
+ // Should have a name, language and file extension
+ if (parts.Count >= 3)
+ {
+ info.Language = parts[parts.Count - 2];
+ }
+
+ return info;
+ }
+
+ private string[] GetFlags(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ // Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _.
+
+ var file = Path.GetFileName(path);
+
+ return file.Split(_options.SubtitleFlagDelimiters, StringSplitOptions.RemoveEmptyEntries);
+ }
+ }
+}
diff --git a/Emby.Naming/TV/EpisodeInfo.cs b/Emby.Naming/TV/EpisodeInfo.cs
new file mode 100644
index 000000000..41397480e
--- /dev/null
+++ b/Emby.Naming/TV/EpisodeInfo.cs
@@ -0,0 +1,51 @@
+
+namespace Emby.Naming.TV
+{
+ public class EpisodeInfo
+ {
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ public string Path { get; set; }
+ /// <summary>
+ /// Gets or sets the container.
+ /// </summary>
+ /// <value>The container.</value>
+ public string Container { get; set; }
+ /// <summary>
+ /// Gets or sets the name of the series.
+ /// </summary>
+ /// <value>The name of the series.</value>
+ public string SeriesName { get; set; }
+ /// <summary>
+ /// Gets or sets the format3 d.
+ /// </summary>
+ /// <value>The format3 d.</value>
+ public string Format3D { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether [is3 d].
+ /// </summary>
+ /// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
+ public bool Is3D { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is stub.
+ /// </summary>
+ /// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
+ public bool IsStub { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the stub.
+ /// </summary>
+ /// <value>The type of the stub.</value>
+ public string StubType { get; set; }
+
+ public int? SeasonNumber { get; set; }
+ public int? EpisodeNumber { get; set; }
+ public int? EndingEpsiodeNumber { get; set; }
+
+ public int? Year { get; set; }
+ public int? Month { get; set; }
+ public int? Day { get; set; }
+ public bool IsByDate { get; set; }
+ }
+}
diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs
new file mode 100644
index 000000000..7f8a6a70e
--- /dev/null
+++ b/Emby.Naming/TV/EpisodePathParser.cs
@@ -0,0 +1,223 @@
+using Emby.Naming.Common;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.TV
+{
+ public class EpisodePathParser
+ {
+ private readonly NamingOptions _options;
+
+ public EpisodePathParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public EpisodePathParserResult Parse(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true)
+ {
+ // Added to be able to use regex patterns which require a file extension.
+ // There were no failed tests without this block, but to be safe, we can keep it until
+ // the regex which require file extensions are modified so that they don't need them.
+ if (IsDirectory)
+ path += ".mp4";
+
+ EpisodePathParserResult result = null;
+
+ foreach (var expression in _options.EpisodeExpressions)
+ {
+ if (supportsAbsoluteNumbers.HasValue)
+ {
+ if (expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value)
+ {
+ continue;
+ }
+ }
+ if (isNamed.HasValue)
+ {
+ if (expression.IsNamed != isNamed.Value)
+ {
+ continue;
+ }
+ }
+ if (isOptimistic.HasValue)
+ {
+ if (expression.IsOptimistic != isOptimistic.Value)
+ {
+ continue;
+ }
+ }
+
+ var currentResult = Parse(path, expression);
+ if (currentResult.Success)
+ {
+ result = currentResult;
+ break;
+ }
+ }
+
+ if (result != null && fillExtendedInfo)
+ {
+ FillAdditional(path, result);
+
+ if (!string.IsNullOrEmpty(result.SeriesName))
+ {
+ result.SeriesName = result.SeriesName
+ .Trim()
+ .Trim(new[] { '_', '.', '-' })
+ .Trim();
+ }
+ }
+
+ return result ?? new EpisodePathParserResult();
+ }
+
+ private EpisodePathParserResult Parse(string name, EpisodeExpression expression)
+ {
+ var result = new EpisodePathParserResult();
+
+ // This is a hack to handle wmc naming
+ if (expression.IsByDate)
+ {
+ name = name.Replace('_', '-');
+ }
+
+ var match = expression.Regex.Match(name);
+
+ // (Full)(Season)(Episode)(Extension)
+ if (match.Success && match.Groups.Count >= 3)
+ {
+ if (expression.IsByDate)
+ {
+ DateTime date;
+ if (expression.DateTimeFormats.Length > 0)
+ {
+ if (DateTime.TryParseExact(match.Groups[0].Value,
+ expression.DateTimeFormats,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.None,
+ out date))
+ {
+ result.Year = date.Year;
+ result.Month = date.Month;
+ result.Day = date.Day;
+ result.Success = true;
+ }
+ }
+ else
+ {
+ if (DateTime.TryParse(match.Groups[0].Value, out date))
+ {
+ result.Year = date.Year;
+ result.Month = date.Month;
+ result.Day = date.Day;
+ result.Success = true;
+ }
+ }
+
+ // TODO: Only consider success if date successfully parsed?
+ result.Success = true;
+ }
+ else if (expression.IsNamed)
+ {
+ int num;
+ if (int.TryParse(match.Groups["seasonnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ {
+ result.SeasonNumber = num;
+ }
+
+ if (int.TryParse(match.Groups["epnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ {
+ result.EpisodeNumber = num;
+ }
+
+ Group endingNumberGroup = match.Groups["endingepnumber"];
+ if (endingNumberGroup.Success)
+ {
+ // Will only set EndingEpsiodeNumber if the captured number is not followed by additional numbers
+ // or a 'p' or 'i' as what you would get with a pixel resolution specification.
+ // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
+ int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
+ if (nextIndex >= name.Length || "0123456789iIpP".IndexOf(name[nextIndex]) == -1)
+ {
+ if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ {
+ result.EndingEpsiodeNumber = num;
+ }
+ }
+ }
+
+ result.SeriesName = match.Groups["seriesname"].Value;
+ result.Success = result.EpisodeNumber.HasValue;
+ }
+ else
+ {
+ int num;
+ if (int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ {
+ result.SeasonNumber = num;
+ }
+ if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ {
+ result.EpisodeNumber = num;
+ }
+
+ result.Success = result.EpisodeNumber.HasValue;
+ }
+
+ // Invalidate match when the season is 200 through 1927 or above 2500
+ // because it is an error unless the TV show is intentionally using false season numbers.
+ // It avoids erroneous parsing of something like "Series Special (1920x1080).mkv" as being season 1920 episode 1080.
+ if (result.SeasonNumber >= 200 && result.SeasonNumber < 1928 || result.SeasonNumber > 2500)
+ result.Success = false;
+
+ result.IsByDate = expression.IsByDate;
+ }
+
+ return result;
+ }
+
+ private void FillAdditional(string path, EpisodePathParserResult info)
+ {
+ var expressions = _options.MultipleEpisodeExpressions.ToList();
+
+ if (string.IsNullOrEmpty(info.SeriesName))
+ {
+ expressions.InsertRange(0, _options.EpisodeExpressions.Where(i => i.IsNamed));
+ }
+
+ FillAdditional(path, info, expressions);
+ }
+
+ private void FillAdditional(string path, EpisodePathParserResult info, IEnumerable<EpisodeExpression> expressions)
+ {
+ var results = expressions
+ .Where(i => i.IsNamed)
+ .Select(i => Parse(path, i))
+ .Where(i => i.Success);
+
+ foreach (var result in results)
+ {
+ if (string.IsNullOrEmpty(info.SeriesName))
+ {
+ info.SeriesName = result.SeriesName;
+ }
+
+ if (!info.EndingEpsiodeNumber.HasValue && info.EpisodeNumber.HasValue)
+ {
+ info.EndingEpsiodeNumber = result.EndingEpsiodeNumber;
+ }
+
+ if (!string.IsNullOrEmpty(info.SeriesName))
+ {
+ if (!info.EpisodeNumber.HasValue || info.EndingEpsiodeNumber.HasValue)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Emby.Naming/TV/EpisodePathParserResult.cs b/Emby.Naming/TV/EpisodePathParserResult.cs
new file mode 100644
index 000000000..9890c78a4
--- /dev/null
+++ b/Emby.Naming/TV/EpisodePathParserResult.cs
@@ -0,0 +1,17 @@
+
+namespace Emby.Naming.TV
+{
+ public class EpisodePathParserResult
+ {
+ public int? SeasonNumber { get; set; }
+ public int? EpisodeNumber { get; set; }
+ public int? EndingEpsiodeNumber { get; set; }
+ public string SeriesName { get; set; }
+ public bool Success { get; set; }
+
+ public bool IsByDate { get; set; }
+ public int? Year { get; set; }
+ public int? Month { get; set; }
+ public int? Day { get; set; }
+ }
+}
diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs
new file mode 100644
index 000000000..2007d1307
--- /dev/null
+++ b/Emby.Naming/TV/EpisodeResolver.cs
@@ -0,0 +1,76 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Emby.Naming.TV
+{
+ public class EpisodeResolver
+ {
+ private readonly NamingOptions _options;
+
+ public EpisodeResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public EpisodeInfo Resolve(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var isStub = false;
+ string container = null;
+ string stubType = null;
+
+ if (!IsDirectory)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ // Check supported extensions
+ if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ var stubResult = new StubResolver(_options).ResolveFile(path);
+
+ isStub = stubResult.IsStub;
+
+ // It's not supported. Check stub extensions
+ if (!isStub)
+ {
+ return null;
+ }
+
+ stubType = stubResult.StubType;
+ }
+
+ container = extension.TrimStart('.');
+ }
+
+ var flags = new FlagParser(_options).GetFlags(path);
+ var format3DResult = new Format3DParser(_options).Parse(flags);
+
+ var parsingResult = new EpisodePathParser(_options)
+ .Parse(path, IsDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo);
+
+ return new EpisodeInfo
+ {
+ Path = path,
+ Container = container,
+ IsStub = isStub,
+ EndingEpsiodeNumber = parsingResult.EndingEpsiodeNumber,
+ EpisodeNumber = parsingResult.EpisodeNumber,
+ SeasonNumber = parsingResult.SeasonNumber,
+ SeriesName = parsingResult.SeriesName,
+ StubType = stubType,
+ Is3D = format3DResult.Is3D,
+ Format3D = format3DResult.Format3D,
+ IsByDate = parsingResult.IsByDate,
+ Day = parsingResult.Day,
+ Month = parsingResult.Month,
+ Year = parsingResult.Year
+ };
+ }
+ }
+}
diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs
new file mode 100644
index 000000000..72277e6b9
--- /dev/null
+++ b/Emby.Naming/TV/SeasonPathParser.cs
@@ -0,0 +1,186 @@
+using Emby.Naming.Common;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+
+namespace Emby.Naming.TV
+{
+ public class SeasonPathParser
+ {
+ private readonly NamingOptions _options;
+
+ public SeasonPathParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
+ {
+ var result = new SeasonPathParserResult();
+
+ var seasonNumberInfo = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders);
+
+ result.SeasonNumber = seasonNumberInfo.Item1;
+
+ if (result.SeasonNumber.HasValue)
+ {
+ result.Success = true;
+ result.IsSeasonFolder = seasonNumberInfo.Item2;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// A season folder must contain one of these somewhere in the name
+ /// </summary>
+ private static readonly string[] SeasonFolderNames =
+ {
+ "season",
+ "sæson",
+ "temporada",
+ "saison",
+ "staffel",
+ "series",
+ "сезон",
+ "stagione"
+ };
+
+ /// <summary>
+ /// Gets the season number from path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="supportSpecialAliases">if set to <c>true</c> [support special aliases].</param>
+ /// <param name="supportNumericSeasonFolders">if set to <c>true</c> [support numeric season folders].</param>
+ /// <returns>System.Nullable{System.Int32}.</returns>
+ private Tuple<int?, bool> GetSeasonNumberFromPath(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
+ {
+ var filename = Path.GetFileName(path);
+
+ if (supportSpecialAliases)
+ {
+ if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Tuple<int?, bool>(0, true);
+ }
+ if (string.Equals(filename, "extras", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Tuple<int?, bool>(0, true);
+ }
+ }
+
+ if (supportNumericSeasonFolders)
+ {
+ int val;
+ if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+ {
+ return new Tuple<int?, bool>(val, true);
+ }
+ }
+
+ if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase))
+ {
+ var testFilename = filename.Substring(1);
+
+ int val;
+ if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+ {
+ return new Tuple<int?, bool>(val, true);
+ }
+ }
+
+ // Look for one of the season folder names
+ foreach (var name in SeasonFolderNames)
+ {
+ var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase);
+
+ if (index != -1)
+ {
+ var result = GetSeasonNumberFromPathSubstring(filename.Replace(name, " ", StringComparison.OrdinalIgnoreCase));
+ if (result.Item1.HasValue)
+ {
+ return result;
+ }
+
+ break;
+ }
+ }
+
+ var parts = filename.Split(new[] { '.', '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries);
+ var resultNumber = parts.Select(GetSeasonNumberFromPart).FirstOrDefault(i => i.HasValue);
+ return new Tuple<int?, bool>(resultNumber, true);
+ }
+
+ private int? GetSeasonNumberFromPart(string part)
+ {
+ if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ part = part.Substring(1);
+
+ int value;
+ if (int.TryParse(part, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
+ {
+ return value;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Extracts the season number from the second half of the Season folder name (everything after "Season", or "Staffel")
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>System.Nullable{System.Int32}.</returns>
+ private Tuple<int?, bool> GetSeasonNumberFromPathSubstring(string path)
+ {
+ var numericStart = -1;
+ var length = 0;
+
+ var hasOpenParenth = false;
+ var isSeasonFolder = true;
+
+ // Find out where the numbers start, and then keep going until they end
+ for (var i = 0; i < path.Length; i++)
+ {
+ if (char.IsNumber(path, i))
+ {
+ if (!hasOpenParenth)
+ {
+ if (numericStart == -1)
+ {
+ numericStart = i;
+ }
+ length++;
+ }
+ }
+ else if (numericStart != -1)
+ {
+ // There's other stuff after the season number, e.g. episode number
+ isSeasonFolder = false;
+ break;
+ }
+
+ var currentChar = path[i];
+ if (currentChar.Equals('('))
+ {
+ hasOpenParenth = true;
+ }
+ else if (currentChar.Equals(')'))
+ {
+ hasOpenParenth = false;
+ }
+ }
+
+ if (numericStart == -1)
+ {
+ return new Tuple<int?, bool>(null, isSeasonFolder);
+ }
+
+ return new Tuple<int?, bool>(int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder);
+ }
+ }
+}
diff --git a/Emby.Naming/TV/SeasonPathParserResult.cs b/Emby.Naming/TV/SeasonPathParserResult.cs
new file mode 100644
index 000000000..9a18d0a03
--- /dev/null
+++ b/Emby.Naming/TV/SeasonPathParserResult.cs
@@ -0,0 +1,18 @@
+
+namespace Emby.Naming.TV
+{
+ public class SeasonPathParserResult
+ {
+ /// <summary>
+ /// Gets or sets the season number.
+ /// </summary>
+ /// <value>The season number.</value>
+ public int? SeasonNumber { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="SeasonPathParserResult"/> is success.
+ /// </summary>
+ /// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
+ public bool Success { get; set; }
+ public bool IsSeasonFolder { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs
new file mode 100644
index 000000000..572dd1c60
--- /dev/null
+++ b/Emby.Naming/Video/CleanDateTimeParser.cs
@@ -0,0 +1,87 @@
+using System;
+using Emby.Naming.Common;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Video
+{
+ /// <summary>
+ /// http://kodi.wiki/view/Advancedsettings.xml#video
+ /// </summary>
+ public class CleanDateTimeParser
+ {
+ private readonly NamingOptions _options;
+
+ public CleanDateTimeParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public CleanDateTimeResult Clean(string name)
+ {
+ var originalName = name;
+
+ try
+ {
+ var extension = Path.GetExtension(name) ?? string.Empty;
+ // Check supported extensions
+ if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) &&
+ !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ // Dummy up a file extension because the expressions will fail without one
+ // This is tricky because we can't just check Path.GetExtension for empty
+ // If the input is "St. Vincent (2014)", it will produce ". Vincent (2014)" as the extension
+ name += ".mkv";
+ }
+ }
+ catch (ArgumentException)
+ {
+
+ }
+
+ var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i))
+ .FirstOrDefault(i => i.HasChanged) ??
+ new CleanDateTimeResult { Name = originalName };
+
+ if (result.HasChanged)
+ {
+ return result;
+ }
+
+ // Make a second pass, running clean string first
+ var cleanStringResult = new CleanStringParser().Clean(name, _options.CleanStringRegexes);
+
+ if (!cleanStringResult.HasChanged)
+ {
+ return result;
+ }
+
+ return _options.CleanDateTimeRegexes.Select(i => Clean(cleanStringResult.Name, i))
+ .FirstOrDefault(i => i.HasChanged) ??
+ result;
+ }
+
+ private CleanDateTimeResult Clean(string name, Regex expression)
+ {
+ var result = new CleanDateTimeResult();
+
+ var match = expression.Match(name);
+
+ if (match.Success && match.Groups.Count == 4)
+ {
+ int year;
+ if (match.Groups[1].Success && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out year))
+ {
+ name = match.Groups[1].Value;
+ result.Year = year;
+ result.HasChanged = true;
+ }
+ }
+
+ result.Name = name;
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/CleanDateTimeResult.cs b/Emby.Naming/Video/CleanDateTimeResult.cs
new file mode 100644
index 000000000..946fd953c
--- /dev/null
+++ b/Emby.Naming/Video/CleanDateTimeResult.cs
@@ -0,0 +1,22 @@
+
+namespace Emby.Naming.Video
+{
+ public class CleanDateTimeResult
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the year.
+ /// </summary>
+ /// <value>The year.</value>
+ public int? Year { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has changed.
+ /// </summary>
+ /// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
+ public bool HasChanged { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs
new file mode 100644
index 000000000..bddf9589b
--- /dev/null
+++ b/Emby.Naming/Video/CleanStringParser.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Video
+{
+ /// <summary>
+ /// http://kodi.wiki/view/Advancedsettings.xml#video
+ /// </summary>
+ public class CleanStringParser
+ {
+ public CleanStringResult Clean(string name, IEnumerable<Regex> expressions)
+ {
+ var hasChanged = false;
+
+ foreach (var exp in expressions)
+ {
+ var result = Clean(name, exp);
+
+ if (!string.IsNullOrEmpty(result.Name))
+ {
+ name = result.Name;
+ hasChanged = hasChanged || result.HasChanged;
+ }
+ }
+
+ return new CleanStringResult
+ {
+ Name = name,
+ HasChanged = hasChanged
+ };
+ }
+
+ private CleanStringResult Clean(string name, Regex expression)
+ {
+ var result = new CleanStringResult();
+
+ var match = expression.Match(name);
+
+ if (match.Success)
+ {
+ result.HasChanged = true;
+ name = name.Substring(0, match.Index);
+ }
+
+ result.Name = name;
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/CleanStringResult.cs b/Emby.Naming/Video/CleanStringResult.cs
new file mode 100644
index 000000000..0282863e0
--- /dev/null
+++ b/Emby.Naming/Video/CleanStringResult.cs
@@ -0,0 +1,17 @@
+
+namespace Emby.Naming.Video
+{
+ public class CleanStringResult
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has changed.
+ /// </summary>
+ /// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
+ public bool HasChanged { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs
new file mode 100644
index 000000000..bde1a4765
--- /dev/null
+++ b/Emby.Naming/Video/ExtraResolver.cs
@@ -0,0 +1,87 @@
+using Emby.Naming.Audio;
+using Emby.Naming.Common;
+using System;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Video
+{
+ public class ExtraResolver
+ {
+ private readonly NamingOptions _options;
+
+ public ExtraResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public ExtraResult GetExtraInfo(string path)
+ {
+ return _options.VideoExtraRules
+ .Select(i => GetExtraInfo(path, i))
+ .FirstOrDefault(i => !string.IsNullOrEmpty(i.ExtraType)) ?? new ExtraResult();
+ }
+
+ private ExtraResult GetExtraInfo(string path, ExtraRule rule)
+ {
+ var result = new ExtraResult();
+
+ if (rule.MediaType == MediaType.Audio)
+ {
+ if (!new AudioFileParser(_options).IsAudioFile(path))
+ {
+ return result;
+ }
+ }
+ else if (rule.MediaType == MediaType.Video)
+ {
+ if (!new VideoResolver(_options).IsVideoFile(path))
+ {
+ return result;
+ }
+ }
+ else
+ {
+ return result;
+ }
+
+ if (rule.RuleType == ExtraRuleType.Filename)
+ {
+ var filename = Path.GetFileNameWithoutExtension(path);
+
+ if (string.Equals(filename, rule.Token, StringComparison.OrdinalIgnoreCase))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+
+ else if (rule.RuleType == ExtraRuleType.Suffix)
+ {
+ var filename = Path.GetFileNameWithoutExtension(path);
+
+ if (filename.IndexOf(rule.Token, StringComparison.OrdinalIgnoreCase) > 0)
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+
+ else if (rule.RuleType == ExtraRuleType.Regex)
+ {
+ var filename = Path.GetFileName(path);
+
+ var regex = new Regex(rule.Token, RegexOptions.IgnoreCase);
+
+ if (regex.IsMatch(filename))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/ExtraResult.cs b/Emby.Naming/Video/ExtraResult.cs
new file mode 100644
index 000000000..ca79af9da
--- /dev/null
+++ b/Emby.Naming/Video/ExtraResult.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Emby.Naming.Video
+{
+ public class ExtraResult
+ {
+ /// <summary>
+ /// Gets or sets the type of the extra.
+ /// </summary>
+ /// <value>The type of the extra.</value>
+ public string ExtraType { get; set; }
+ /// <summary>
+ /// Gets or sets the rule.
+ /// </summary>
+ /// <value>The rule.</value>
+ public ExtraRule Rule { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/ExtraRule.cs b/Emby.Naming/Video/ExtraRule.cs
new file mode 100644
index 000000000..ef83b3cd6
--- /dev/null
+++ b/Emby.Naming/Video/ExtraRule.cs
@@ -0,0 +1,28 @@
+using Emby.Naming.Common;
+
+namespace Emby.Naming.Video
+{
+ public class ExtraRule
+ {
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ /// <value>The token.</value>
+ public string Token { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the extra.
+ /// </summary>
+ /// <value>The type of the extra.</value>
+ public string ExtraType { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the rule.
+ /// </summary>
+ /// <value>The type of the rule.</value>
+ public ExtraRuleType RuleType { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the media.
+ /// </summary>
+ /// <value>The type of the media.</value>
+ public MediaType MediaType { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/ExtraRuleType.cs b/Emby.Naming/Video/ExtraRuleType.cs
new file mode 100644
index 000000000..323c7cef6
--- /dev/null
+++ b/Emby.Naming/Video/ExtraRuleType.cs
@@ -0,0 +1,19 @@
+
+namespace Emby.Naming.Video
+{
+ public enum ExtraRuleType
+ {
+ /// <summary>
+ /// The suffix
+ /// </summary>
+ Suffix = 0,
+ /// <summary>
+ /// The filename
+ /// </summary>
+ Filename = 1,
+ /// <summary>
+ /// The regex
+ /// </summary>
+ Regex = 2
+ }
+}
diff --git a/Emby.Naming/Video/FileStack.cs b/Emby.Naming/Video/FileStack.cs
new file mode 100644
index 000000000..2feea4cb3
--- /dev/null
+++ b/Emby.Naming/Video/FileStack.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Emby.Naming.Video
+{
+ public class FileStack
+ {
+ public string Name { get; set; }
+ public List<string> Files { get; set; }
+ public bool IsDirectoryStack { get; set; }
+
+ public FileStack()
+ {
+ Files = new List<string>();
+ }
+
+ public bool ContainsFile(string file, bool IsDirectory)
+ {
+ if (IsDirectoryStack == IsDirectory)
+ {
+ return Files.Contains(file, StringComparer.OrdinalIgnoreCase);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/FlagParser.cs b/Emby.Naming/Video/FlagParser.cs
new file mode 100644
index 000000000..a2c541eeb
--- /dev/null
+++ b/Emby.Naming/Video/FlagParser.cs
@@ -0,0 +1,35 @@
+using Emby.Naming.Common;
+using System;
+using System.IO;
+
+namespace Emby.Naming.Video
+{
+ public class FlagParser
+ {
+ private readonly NamingOptions _options;
+
+ public FlagParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public string[] GetFlags(string path)
+ {
+ return GetFlags(path, _options.VideoFlagDelimiters);
+ }
+
+ public string[] GetFlags(string path, char[] delimeters)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ // Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _.
+
+ var file = Path.GetFileName(path);
+
+ return file.Split(delimeters, StringSplitOptions.RemoveEmptyEntries);
+ }
+ }
+}
diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs
new file mode 100644
index 000000000..42737b483
--- /dev/null
+++ b/Emby.Naming/Video/Format3DParser.cs
@@ -0,0 +1,81 @@
+using Emby.Naming.Common;
+using System;
+using System.Linq;
+
+namespace Emby.Naming.Video
+{
+ public class Format3DParser
+ {
+ private readonly NamingOptions _options;
+
+ public Format3DParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public Format3DResult Parse(string path)
+ {
+ var delimeters = _options.VideoFlagDelimiters.ToList();
+ delimeters.Add(' ');
+
+ return Parse(new FlagParser(_options).GetFlags(path, delimeters.ToArray()));
+ }
+
+ internal Format3DResult Parse(string[] videoFlags)
+ {
+ foreach (var rule in _options.Format3DRules)
+ {
+ var result = Parse(videoFlags, rule);
+
+ if (result.Is3D)
+ {
+ return result;
+ }
+ }
+
+ return new Format3DResult();
+ }
+
+ private Format3DResult Parse(string[] videoFlags, Format3DRule rule)
+ {
+ var result = new Format3DResult();
+
+ if (string.IsNullOrEmpty(rule.PreceedingToken))
+ {
+ result.Format3D = new[] { rule.Token }.FirstOrDefault(i => videoFlags.Contains(i, StringComparer.OrdinalIgnoreCase));
+ result.Is3D = !string.IsNullOrEmpty(result.Format3D);
+
+ if (result.Is3D)
+ {
+ result.Tokens.Add(rule.Token);
+ }
+ }
+ else
+ {
+ var foundPrefix = false;
+ string format = null;
+
+ foreach (var flag in videoFlags)
+ {
+ if (foundPrefix)
+ {
+ result.Tokens.Add(rule.PreceedingToken);
+
+ if (string.Equals(rule.Token, flag, StringComparison.OrdinalIgnoreCase))
+ {
+ format = flag;
+ result.Tokens.Add(rule.Token);
+ }
+ break;
+ }
+ foundPrefix = string.Equals(flag, rule.PreceedingToken, StringComparison.OrdinalIgnoreCase);
+ }
+
+ result.Is3D = foundPrefix && !string.IsNullOrEmpty(format);
+ result.Format3D = format;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs
new file mode 100644
index 000000000..147ccfc05
--- /dev/null
+++ b/Emby.Naming/Video/Format3DResult.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+
+namespace Emby.Naming.Video
+{
+ public class Format3DResult
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether [is3 d].
+ /// </summary>
+ /// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
+ public bool Is3D { get; set; }
+ /// <summary>
+ /// Gets or sets the format3 d.
+ /// </summary>
+ /// <value>The format3 d.</value>
+ public string Format3D { get; set; }
+ /// <summary>
+ /// Gets or sets the tokens.
+ /// </summary>
+ /// <value>The tokens.</value>
+ public List<string> Tokens { get; set; }
+
+ public Format3DResult()
+ {
+ Tokens = new List<string>();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/Format3DRule.cs b/Emby.Naming/Video/Format3DRule.cs
new file mode 100644
index 000000000..3c173efbc
--- /dev/null
+++ b/Emby.Naming/Video/Format3DRule.cs
@@ -0,0 +1,17 @@
+
+namespace Emby.Naming.Video
+{
+ public class Format3DRule
+ {
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ /// <value>The token.</value>
+ public string Token { get; set; }
+ /// <summary>
+ /// Gets or sets the preceeding token.
+ /// </summary>
+ /// <value>The preceeding token.</value>
+ public string PreceedingToken { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs
new file mode 100644
index 000000000..2a7125536
--- /dev/null
+++ b/Emby.Naming/Video/StackResolver.cs
@@ -0,0 +1,216 @@
+using Emby.Naming.Common;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Naming.Video
+{
+ public class StackResolver
+ {
+ private readonly NamingOptions _options;
+
+ public StackResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public StackResult ResolveDirectories(IEnumerable<string> files)
+ {
+ return Resolve(files.Select(i => new FileSystemMetadata
+ {
+ FullName = i,
+ IsDirectory = true
+ }));
+ }
+
+ public StackResult ResolveFiles(IEnumerable<string> files)
+ {
+ return Resolve(files.Select(i => new FileSystemMetadata
+ {
+ FullName = i,
+ IsDirectory = false
+ }));
+ }
+
+ public StackResult ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
+ {
+ var result = new StackResult();
+ foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName)))
+ {
+ var stack = new FileStack();
+ stack.Name = Path.GetFileName(directory.Key);
+ stack.IsDirectoryStack = false;
+ foreach (var file in directory)
+ {
+ if (file.IsDirectory)
+ continue;
+ stack.Files.Add(file.FullName);
+ }
+ result.Stacks.Add(stack);
+ }
+ return result;
+ }
+
+ public StackResult Resolve(IEnumerable<FileSystemMetadata> files)
+ {
+ var result = new StackResult();
+
+ var resolver = new VideoResolver(_options);
+
+ var list = files
+ .Where(i => i.IsDirectory || (resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)))
+ .OrderBy(i => i.FullName)
+ .ToList();
+
+ var expressions = _options.VideoFileStackingRegexes;
+
+ for (var i = 0; i < list.Count; i++)
+ {
+ var offset = 0;
+
+ var file1 = list[i];
+
+ var expressionIndex = 0;
+ while (expressionIndex < expressions.Length)
+ {
+ var exp = expressions[expressionIndex];
+ var stack = new FileStack();
+
+ // (Title)(Volume)(Ignore)(Extension)
+ var match1 = FindMatch(file1, exp, offset);
+
+ if (match1.Success)
+ {
+ var title1 = match1.Groups[1].Value;
+ var volume1 = match1.Groups[2].Value;
+ var ignore1 = match1.Groups[3].Value;
+ var extension1 = match1.Groups[4].Value;
+
+ var j = i + 1;
+ while (j < list.Count)
+ {
+ var file2 = list[j];
+
+ if (file1.IsDirectory != file2.IsDirectory)
+ {
+ j++;
+ continue;
+ }
+
+ // (Title)(Volume)(Ignore)(Extension)
+ var match2 = FindMatch(file2, exp, offset);
+
+ if (match2.Success)
+ {
+ var title2 = match2.Groups[1].Value;
+ var volume2 = match2.Groups[2].Value;
+ var ignore2 = match2.Groups[3].Value;
+ var extension2 = match2.Groups[4].Value;
+
+ if (string.Equals(title1, title2, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase))
+ {
+ if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase))
+ {
+ if (stack.Files.Count == 0)
+ {
+ stack.Name = title1 + ignore1;
+ stack.IsDirectoryStack = file1.IsDirectory;
+ //stack.Name = title1 + ignore1 + extension1;
+ stack.Files.Add(file1.FullName);
+ }
+ stack.Files.Add(file2.FullName);
+ }
+ else
+ {
+ // Sequel
+ offset = 0;
+ expressionIndex++;
+ break;
+ }
+ }
+ else if (!string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase))
+ {
+ // False positive, try again with offset
+ offset = match1.Groups[3].Index;
+ break;
+ }
+ else
+ {
+ // Extension mismatch
+ offset = 0;
+ expressionIndex++;
+ break;
+ }
+ }
+ else
+ {
+ // Title mismatch
+ offset = 0;
+ expressionIndex++;
+ break;
+ }
+ }
+ else
+ {
+ // No match 2, next expression
+ offset = 0;
+ expressionIndex++;
+ break;
+ }
+
+ j++;
+ }
+
+ if (j == list.Count)
+ {
+ expressionIndex = expressions.Length;
+ }
+ }
+ else
+ {
+ // No match 1
+ offset = 0;
+ expressionIndex++;
+ }
+
+ if (stack.Files.Count > 1)
+ {
+ result.Stacks.Add(stack);
+ i += stack.Files.Count - 1;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private string GetRegexInput(FileSystemMetadata file)
+ {
+ // For directories, dummy up an extension otherwise the expressions will fail
+ var input = !file.IsDirectory
+ ? file.FullName
+ : file.FullName + ".mkv";
+
+ return Path.GetFileName(input);
+ }
+
+ private Match FindMatch(FileSystemMetadata input, Regex regex, int offset)
+ {
+ var regexInput = GetRegexInput(input);
+
+ if (offset < 0 || offset >= regexInput.Length)
+ {
+ return Match.Empty;
+ }
+
+ return regex.Match(regexInput, offset);
+ }
+ }
+}
diff --git a/Emby.Naming/Video/StackResult.cs b/Emby.Naming/Video/StackResult.cs
new file mode 100644
index 000000000..920a7dea7
--- /dev/null
+++ b/Emby.Naming/Video/StackResult.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace Emby.Naming.Video
+{
+ public class StackResult
+ {
+ public List<FileStack> Stacks { get; set; }
+
+ public StackResult()
+ {
+ Stacks = new List<FileStack>();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs
new file mode 100644
index 000000000..69f1f50fa
--- /dev/null
+++ b/Emby.Naming/Video/StubResolver.cs
@@ -0,0 +1,44 @@
+using Emby.Naming.Common;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Emby.Naming.Video
+{
+ public class StubResolver
+ {
+ private readonly NamingOptions _options;
+
+ public StubResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public StubResult ResolveFile(string path)
+ {
+ var result = new StubResult();
+ var extension = Path.GetExtension(path) ?? string.Empty;
+
+ if (_options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ result.IsStub = true;
+
+ path = Path.GetFileNameWithoutExtension(path);
+
+ var token = (Path.GetExtension(path) ?? string.Empty).TrimStart('.');
+
+ foreach (var rule in _options.StubTypes)
+ {
+ if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
+ {
+ result.StubType = rule.StubType;
+ result.Tokens.Add(token);
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/Video/StubResult.cs b/Emby.Naming/Video/StubResult.cs
new file mode 100644
index 000000000..c9d06c9a7
--- /dev/null
+++ b/Emby.Naming/Video/StubResult.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+
+namespace Emby.Naming.Video
+{
+ public class StubResult
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is stub.
+ /// </summary>
+ /// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
+ public bool IsStub { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the stub.
+ /// </summary>
+ /// <value>The type of the stub.</value>
+ public string StubType { get; set; }
+ /// <summary>
+ /// Gets or sets the tokens.
+ /// </summary>
+ /// <value>The tokens.</value>
+ public List<string> Tokens { get; set; }
+
+ public StubResult()
+ {
+ Tokens = new List<string>();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/StubTypeRule.cs b/Emby.Naming/Video/StubTypeRule.cs
new file mode 100644
index 000000000..66ebfc3a2
--- /dev/null
+++ b/Emby.Naming/Video/StubTypeRule.cs
@@ -0,0 +1,17 @@
+
+namespace Emby.Naming.Video
+{
+ public class StubTypeRule
+ {
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ /// <value>The token.</value>
+ public string Token { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the stub.
+ /// </summary>
+ /// <value>The type of the stub.</value>
+ public string StubType { get; set; }
+ }
+}
diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs
new file mode 100644
index 000000000..96839c31e
--- /dev/null
+++ b/Emby.Naming/Video/VideoFileInfo.cs
@@ -0,0 +1,79 @@
+
+namespace Emby.Naming.Video
+{
+ /// <summary>
+ /// Represents a single video file
+ /// </summary>
+ public class VideoFileInfo
+ {
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ public string Path { get; set; }
+ /// <summary>
+ /// Gets or sets the container.
+ /// </summary>
+ /// <value>The container.</value>
+ public string Container { get; set; }
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the year.
+ /// </summary>
+ /// <value>The year.</value>
+ public int? Year { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc.
+ /// </summary>
+ /// <value>The type of the extra.</value>
+ public string ExtraType { get; set; }
+ /// <summary>
+ /// Gets or sets the extra rule.
+ /// </summary>
+ /// <value>The extra rule.</value>
+ public ExtraRule ExtraRule { get; set; }
+ /// <summary>
+ /// Gets or sets the format3 d.
+ /// </summary>
+ /// <value>The format3 d.</value>
+ public string Format3D { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether [is3 d].
+ /// </summary>
+ /// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
+ public bool Is3D { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is stub.
+ /// </summary>
+ /// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
+ public bool IsStub { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the stub.
+ /// </summary>
+ /// <value>The type of the stub.</value>
+ public string StubType { get; set; }
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ /// <value>The type.</value>
+ public bool IsDirectory { get; set; }
+ /// <summary>
+ /// Gets the file name without extension.
+ /// </summary>
+ /// <value>The file name without extension.</value>
+ public string FileNameWithoutExtension
+ {
+ get { return !IsDirectory ? System.IO.Path.GetFileNameWithoutExtension(Path) : System.IO.Path.GetFileName(Path); }
+ }
+
+ public override string ToString()
+ {
+ // Makes debugging easier
+ return Name ?? base.ToString();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs
new file mode 100644
index 000000000..f4d311b97
--- /dev/null
+++ b/Emby.Naming/Video/VideoInfo.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+
+namespace Emby.Naming.Video
+{
+ /// <summary>
+ /// Represents a complete video, including all parts and subtitles
+ /// </summary>
+ public class VideoInfo
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the year.
+ /// </summary>
+ /// <value>The year.</value>
+ public int? Year { get; set; }
+ /// <summary>
+ /// Gets or sets the files.
+ /// </summary>
+ /// <value>The files.</value>
+ public List<VideoFileInfo> Files { get; set; }
+ /// <summary>
+ /// Gets or sets the extras.
+ /// </summary>
+ /// <value>The extras.</value>
+ public List<VideoFileInfo> Extras { get; set; }
+ /// <summary>
+ /// Gets or sets the alternate versions.
+ /// </summary>
+ /// <value>The alternate versions.</value>
+ public List<VideoFileInfo> AlternateVersions { get; set; }
+
+ public VideoInfo()
+ {
+ Files = new List<VideoFileInfo>();
+ Extras = new List<VideoFileInfo>();
+ AlternateVersions = new List<VideoFileInfo>();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
new file mode 100644
index 000000000..47be28104
--- /dev/null
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -0,0 +1,259 @@
+using Emby.Naming.Common;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Model.IO;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Video
+{
+ public class VideoListResolver
+ {
+ private readonly NamingOptions _options;
+
+ public VideoListResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public IEnumerable<VideoInfo> Resolve(List<FileSystemMetadata> files, bool supportMultiVersion = true)
+ {
+ var videoResolver = new VideoResolver(_options);
+
+ var videoInfos = files
+ .Select(i => videoResolver.Resolve(i.FullName, i.IsDirectory))
+ .Where(i => i != null)
+ .ToList();
+
+ // Filter out all extras, otherwise they could cause stacks to not be resolved
+ // See the unit test TestStackedWithTrailer
+ var nonExtras = videoInfos
+ .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .Select(i => new FileSystemMetadata
+ {
+ FullName = i.Path,
+ IsDirectory = i.IsDirectory
+ });
+
+ var stackResult = new StackResolver(_options)
+ .Resolve(nonExtras);
+
+ var remainingFiles = videoInfos
+ .Where(i => !stackResult.Stacks.Any(s => s.ContainsFile(i.Path, i.IsDirectory)))
+ .ToList();
+
+ var list = new List<VideoInfo>();
+
+ foreach (var stack in stackResult.Stacks)
+ {
+ var info = new VideoInfo
+ {
+ Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList(),
+ Name = stack.Name
+ };
+
+ info.Year = info.Files.First().Year;
+
+ var extraBaseNames = new List<string>
+ {
+ stack.Name,
+ Path.GetFileNameWithoutExtension(stack.Files[0])
+ };
+
+ var extras = GetExtras(remainingFiles, extraBaseNames);
+
+ if (extras.Count > 0)
+ {
+ remainingFiles = remainingFiles
+ .Except(extras)
+ .ToList();
+
+ info.Extras = extras;
+ }
+
+ list.Add(info);
+ }
+
+ var standaloneMedia = remainingFiles
+ .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .ToList();
+
+ foreach (var media in standaloneMedia)
+ {
+ var info = new VideoInfo
+ {
+ Files = new List<VideoFileInfo> { media },
+ Name = media.Name
+ };
+
+ info.Year = info.Files.First().Year;
+
+ var extras = GetExtras(remainingFiles, new List<string> { media.FileNameWithoutExtension });
+
+ remainingFiles = remainingFiles
+ .Except(extras.Concat(new[] { media }))
+ .ToList();
+
+ info.Extras = extras;
+
+ list.Add(info);
+ }
+
+ if (supportMultiVersion)
+ {
+ list = GetVideosGroupedByVersion(list)
+ .ToList();
+ }
+
+ // If there's only one resolved video, use the folder name as well to find extras
+ if (list.Count == 1)
+ {
+ var info = list[0];
+ var videoPath = list[0].Files[0].Path;
+ var parentPath = Path.GetDirectoryName(videoPath);
+
+ if (!string.IsNullOrEmpty(parentPath))
+ {
+ var folderName = Path.GetFileName(Path.GetDirectoryName(videoPath));
+ if (!string.IsNullOrEmpty(folderName))
+ {
+ var extras = GetExtras(remainingFiles, new List<string> { folderName });
+
+ remainingFiles = remainingFiles
+ .Except(extras)
+ .ToList();
+
+ info.Extras.AddRange(extras);
+ }
+ }
+
+ // Add the extras that are just based on file name as well
+ var extrasByFileName = remainingFiles
+ .Where(i => i.ExtraRule != null && i.ExtraRule.RuleType == ExtraRuleType.Filename)
+ .ToList();
+
+ remainingFiles = remainingFiles
+ .Except(extrasByFileName)
+ .ToList();
+
+ info.Extras.AddRange(extrasByFileName);
+ }
+
+ // If there's only one video, accept all trailers
+ // Be lenient because people use all kinds of mish mash conventions with trailers
+ if (list.Count == 1)
+ {
+ var trailers = remainingFiles
+ .Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ list[0].Extras.AddRange(trailers);
+
+ remainingFiles = remainingFiles
+ .Except(trailers)
+ .ToList();
+ }
+
+ // Whatever files are left, just add them
+ list.AddRange(remainingFiles.Select(i => new VideoInfo
+ {
+ Files = new List<VideoFileInfo> { i },
+ Name = i.Name,
+ Year = i.Year
+ }));
+
+ var orderedList = list.OrderBy(i => i.Name);
+
+ return orderedList;
+ }
+
+ private IEnumerable<VideoInfo> GetVideosGroupedByVersion(List<VideoInfo> videos)
+ {
+ if (videos.Count == 0)
+ {
+ return videos;
+ }
+
+ var list = new List<VideoInfo>();
+
+ var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path));
+
+ if (!string.IsNullOrEmpty(folderName) && folderName.Length > 1)
+ {
+ if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path)))
+ {
+ // Enforce the multi-version limit
+ if (videos.Count <= 8 && HaveSameYear(videos))
+ {
+ var ordered = videos.OrderBy(i => i.Name).ToList();
+
+ list.Add(ordered[0]);
+
+ list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList();
+ list[0].Name = folderName;
+ list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras));
+
+ return list;
+ }
+ }
+ }
+
+ return videos;
+ //foreach (var video in videos.OrderBy(i => i.Name))
+ //{
+ // var match = list
+ // .FirstOrDefault(i => string.Equals(i.Name, video.Name, StringComparison.OrdinalIgnoreCase));
+
+ // if (match != null && video.Files.Count == 1 && match.Files.Count == 1)
+ // {
+ // match.AlternateVersions.Add(video.Files[0]);
+ // match.Extras.AddRange(video.Extras);
+ // }
+ // else
+ // {
+ // list.Add(video);
+ // }
+ //}
+
+ //return list;
+ }
+
+ private bool HaveSameYear(List<VideoInfo> videos)
+ {
+ return videos.Select(i => i.Year ?? -1).Distinct().Count() < 2;
+ }
+
+ private bool IsEligibleForMultiVersion(string folderName, string testFilename)
+ {
+ testFilename = Path.GetFileNameWithoutExtension(testFilename);
+
+ if (string.Equals(folderName, testFilename, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
+ {
+ testFilename = testFilename.Substring(folderName.Length).Trim();
+ return testFilename.StartsWith("-", StringComparison.OrdinalIgnoreCase)||Regex.Replace(testFilename, @"\[([^]]*)\]", "").Trim() == String.Empty;
+ }
+
+ return false;
+ }
+
+ private List<VideoFileInfo> GetExtras(IEnumerable<VideoFileInfo> remainingFiles, List<string> baseNames)
+ {
+ foreach (var name in baseNames.ToList())
+ {
+ var trimmedName = name.TrimEnd().TrimEnd(_options.VideoFlagDelimiters).TrimEnd();
+ baseNames.Add(trimmedName);
+ }
+
+ return remainingFiles
+ .Where(i => !string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
+ .ToList();
+ }
+ }
+}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
new file mode 100644
index 000000000..c4951c728
--- /dev/null
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -0,0 +1,139 @@
+using Emby.Naming.Common;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Emby.Naming.Video
+{
+ public class VideoResolver
+ {
+ private readonly NamingOptions _options;
+
+ public VideoResolver(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ /// <summary>
+ /// Resolves the directory.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>VideoFileInfo.</returns>
+ public VideoFileInfo ResolveDirectory(string path)
+ {
+ return Resolve(path, true);
+ }
+
+ /// <summary>
+ /// Resolves the file.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>VideoFileInfo.</returns>
+ public VideoFileInfo ResolveFile(string path)
+ {
+ return Resolve(path, false);
+ }
+
+ /// <summary>
+ /// Resolves the specified path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="IsDirectory">if set to <c>true</c> [is folder].</param>
+ /// <returns>VideoFileInfo.</returns>
+ /// <exception cref="System.ArgumentNullException">path</exception>
+ public VideoFileInfo Resolve(string path, bool IsDirectory, bool parseName = true)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var isStub = false;
+ string container = null;
+ string stubType = null;
+
+ if (!IsDirectory)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ // Check supported extensions
+ if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ var stubResult = new StubResolver(_options).ResolveFile(path);
+
+ isStub = stubResult.IsStub;
+
+ // It's not supported. Check stub extensions
+ if (!isStub)
+ {
+ return null;
+ }
+
+ stubType = stubResult.StubType;
+ }
+
+ container = extension.TrimStart('.');
+ }
+
+ var flags = new FlagParser(_options).GetFlags(path);
+ var format3DResult = new Format3DParser(_options).Parse(flags);
+
+ var extraResult = new ExtraResolver(_options).GetExtraInfo(path);
+
+ var name = !IsDirectory
+ ? Path.GetFileNameWithoutExtension(path)
+ : Path.GetFileName(path);
+
+ int? year = null;
+
+ if (parseName)
+ {
+ var cleanDateTimeResult = CleanDateTime(name);
+
+ if (string.IsNullOrEmpty(extraResult.ExtraType))
+ {
+ name = cleanDateTimeResult.Name;
+ name = CleanString(name).Name;
+ }
+
+ year = cleanDateTimeResult.Year;
+ }
+
+ return new VideoFileInfo
+ {
+ Path = path,
+ Container = container,
+ IsStub = isStub,
+ Name = name,
+ Year = year,
+ StubType = stubType,
+ Is3D = format3DResult.Is3D,
+ Format3D = format3DResult.Format3D,
+ ExtraType = extraResult.ExtraType,
+ IsDirectory = IsDirectory,
+ ExtraRule = extraResult.Rule
+ };
+ }
+
+ public bool IsVideoFile(string path)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ }
+
+ public bool IsStubFile(string path)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ }
+
+ public CleanStringResult CleanString(string name)
+ {
+ return new CleanStringParser().Clean(name, _options.CleanStringRegexes);
+ }
+
+ public CleanDateTimeResult CleanDateTime(string name)
+ {
+ return new CleanDateTimeParser(_options).Clean(name);
+ }
+ }
+}
diff --git a/MediaBrowser.Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs
index 4876351fc..d09552ebc 100644
--- a/MediaBrowser.Api/NotificationsService.cs
+++ b/Emby.Notifications/Api/NotificationsService.cs
@@ -8,8 +8,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Dto;
-namespace MediaBrowser.Api
+namespace Emby.Notifications.Api
{
[Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")]
public class GetNotifications : IReturn<NotificationResult>
@@ -27,6 +28,37 @@ namespace MediaBrowser.Api
public int? Limit { get; set; }
}
+ public class Notification
+ {
+ public string Id { get; set; }
+
+ public string UserId { get; set; }
+
+ public DateTime Date { get; set; }
+
+ public bool IsRead { get; set; }
+
+ public string Name { get; set; }
+
+ public string Description { get; set; }
+
+ public string Url { get; set; }
+
+ public NotificationLevel Level { get; set; }
+ }
+
+ public class NotificationResult
+ {
+ public Notification[] Notifications { get; set; }
+ public int TotalRecordCount { get; set; }
+ }
+
+ public class NotificationsSummary
+ {
+ public int UnreadCount { get; set; }
+ public NotificationLevel MaxUnreadNotificationLevel { get; set; }
+ }
+
[Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")]
public class GetNotificationsSummary : IReturn<NotificationsSummary>
{
@@ -40,7 +72,7 @@ namespace MediaBrowser.Api
}
[Route("/Notifications/Services", "GET", Summary = "Gets notification types")]
- public class GetNotificationServices : IReturn<List<NotificationServiceInfo>>
+ public class GetNotificationServices : IReturn<List<NameIdPair>>
{
}
@@ -84,49 +116,42 @@ namespace MediaBrowser.Api
}
[Authenticated]
- public class NotificationsService : BaseApiService
+ public class NotificationsService : IService
{
- private readonly INotificationsRepository _notificationsRepo;
private readonly INotificationManager _notificationManager;
private readonly IUserManager _userManager;
- public NotificationsService(INotificationsRepository notificationsRepo, INotificationManager notificationManager, IUserManager userManager)
+ public NotificationsService(INotificationManager notificationManager, IUserManager userManager)
{
- _notificationsRepo = notificationsRepo;
_notificationManager = notificationManager;
_userManager = userManager;
}
public object Get(GetNotificationTypes request)
{
- var result = _notificationManager.GetNotificationTypes();
-
- return ToOptimizedResult(result);
+ return _notificationManager.GetNotificationTypes();
}
public object Get(GetNotificationServices request)
{
- var result = _notificationManager.GetNotificationServices().ToList();
-
- return ToOptimizedResult(result);
+ return _notificationManager.GetNotificationServices().ToList();
}
public object Get(GetNotificationsSummary request)
{
- var result = _notificationsRepo.GetNotificationsSummary(request.UserId);
+ return new NotificationsSummary
+ {
- return ToOptimizedResult(result);
+ };
}
- public void Post(AddAdminNotification request)
+ public Task Post(AddAdminNotification request)
{
// This endpoint really just exists as post of a real with sickbeard
- var task = AddNotification(request);
-
- Task.WaitAll(task);
+ return AddNotification(request);
}
- private async Task AddNotification(AddAdminNotification request)
+ private Task AddNotification(AddAdminNotification request)
{
var notification = new NotificationRequest
{
@@ -135,49 +160,23 @@ namespace MediaBrowser.Api
Level = request.Level,
Name = request.Name,
Url = request.Url,
- UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList()
+ UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray()
};
- await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false);
+ return _notificationManager.SendNotification(notification, CancellationToken.None);
}
public void Post(MarkRead request)
{
- var task = MarkRead(request.Ids, request.UserId, true);
-
- Task.WaitAll(task);
}
public void Post(MarkUnread request)
{
- var task = MarkRead(request.Ids, request.UserId, false);
-
- Task.WaitAll(task);
- }
-
- private Task MarkRead(string idList, string userId, bool read)
- {
- var ids = (idList ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (ids.Length == 0)
- {
- return _notificationsRepo.MarkAllRead(userId, read, CancellationToken.None);
- }
-
- return _notificationsRepo.MarkRead(ids, userId, read, CancellationToken.None);
}
public object Get(GetNotifications request)
{
- var result = _notificationsRepo.GetNotifications(new NotificationQuery
- {
- IsRead = request.IsRead,
- Limit = request.Limit,
- StartIndex = request.StartIndex,
- UserId = request.UserId
- });
-
- return ToOptimizedResult(result);
+ return new NotificationResult();
}
}
}
diff --git a/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs
index b00b5d43b..b45a75b1e 100644
--- a/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
+++ b/Emby.Notifications/CoreNotificationTypes.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Globalization;
-namespace Emby.Server.Implementations.Notifications
+namespace Emby.Notifications
{
public class CoreNotificationTypes : INotificationTypeFactory
{
@@ -25,124 +25,87 @@ namespace Emby.Server.Implementations.Notifications
{
new NotificationTypeInfo
{
- Type = NotificationType.ApplicationUpdateInstalled.ToString(),
- DefaultDescription = "{ReleaseNotes}",
- DefaultTitle = "A new version of Emby Server has been installed.",
- Variables = new string[]{"Version"}
+ Type = NotificationType.ApplicationUpdateInstalled.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.InstallationFailed.ToString(),
- DefaultTitle = "{Name} installation failed.",
- Variables = new string[]{"Name", "Version"}
+ Type = NotificationType.InstallationFailed.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.PluginInstalled.ToString(),
- DefaultTitle = "{Name} was installed.",
- Variables = new string[]{"Name", "Version"}
+ Type = NotificationType.PluginInstalled.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.PluginError.ToString(),
- DefaultTitle = "{Name} has encountered an error.",
- DefaultDescription = "{ErrorMessage}",
- Variables = new string[]{"Name", "ErrorMessage"}
+ Type = NotificationType.PluginError.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.PluginUninstalled.ToString(),
- DefaultTitle = "{Name} was uninstalled.",
- Variables = new string[]{"Name", "Version"}
+ Type = NotificationType.PluginUninstalled.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.PluginUpdateInstalled.ToString(),
- DefaultTitle = "{Name} was updated.",
- DefaultDescription = "{ReleaseNotes}",
- Variables = new string[]{"Name", "ReleaseNotes", "Version"}
+ Type = NotificationType.PluginUpdateInstalled.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.ServerRestartRequired.ToString(),
- DefaultTitle = "Please restart Emby Server to finish updating."
+ Type = NotificationType.ServerRestartRequired.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.TaskFailed.ToString(),
- DefaultTitle = "{Name} failed.",
- DefaultDescription = "{ErrorMessage}",
- Variables = new string[]{"Name", "ErrorMessage"}
+ Type = NotificationType.TaskFailed.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.NewLibraryContent.ToString(),
- DefaultTitle = "{Name} has been added to your media library.",
- Variables = new string[]{"Name"}
+ Type = NotificationType.NewLibraryContent.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.AudioPlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.AudioPlayback.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.GamePlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.GamePlayback.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.VideoPlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.VideoPlayback.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.AudioPlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.AudioPlaybackStopped.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.GamePlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.GamePlaybackStopped.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.VideoPlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
+ Type = NotificationType.VideoPlaybackStopped.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.CameraImageUploaded.ToString(),
- DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
- Variables = new string[]{"DeviceName"}
+ Type = NotificationType.CameraImageUploaded.ToString()
},
new NotificationTypeInfo
{
- Type = NotificationType.UserLockedOut.ToString(),
- DefaultTitle = "{UserName} has been locked out.",
- Variables = new string[]{"UserName"}
+ Type = NotificationType.UserLockedOut.ToString()
}
};
@@ -150,8 +113,7 @@ namespace Emby.Server.Implementations.Notifications
{
knownTypes.Add(new NotificationTypeInfo
{
- Type = NotificationType.ApplicationUpdateAvailable.ToString(),
- DefaultTitle = "A new version of Emby Server is available for download."
+ Type = NotificationType.ApplicationUpdateAvailable.ToString()
});
}
diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj
new file mode 100644
index 000000000..0a07c419b
--- /dev/null
+++ b/Emby.Notifications/Emby.Notifications.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs
index a7c5b1233..2d464910e 100644
--- a/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
+++ b/Emby.Notifications/NotificationConfigurationFactory.cs
@@ -2,13 +2,13 @@
using MediaBrowser.Model.Notifications;
using System.Collections.Generic;
-namespace Emby.Server.Implementations.Notifications
+namespace Emby.Notifications
{
public class NotificationConfigurationFactory : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
- return new List<ConfigurationStore>
+ return new ConfigurationStore[]
{
new ConfigurationStore
{
diff --git a/Emby.Server.Implementations/Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index e11f2790e..bb348d267 100644
--- a/Emby.Server.Implementations/Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -12,8 +12,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Dto;
-namespace Emby.Server.Implementations.Notifications
+namespace Emby.Notifications
{
public class NotificationManager : INotificationManager
{
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.Notifications
{
var notificationType = request.NotificationType;
- var options = string.IsNullOrWhiteSpace(notificationType) ?
+ var options = string.IsNullOrEmpty(notificationType) ?
null :
GetConfiguration().GetOptions(notificationType);
@@ -54,8 +55,8 @@ namespace Emby.Server.Implementations.Notifications
.Where(i => relatedItem == null || relatedItem.IsVisibleStandalone(i))
.ToArray();
- var title = GetTitle(request, options);
- var description = GetDescription(request, options);
+ var title = request.Name;
+ var description = request.Description;
var tasks = _services.Where(i => IsEnabled(i, notificationType))
.Select(i => SendNotification(request, i, users, title, description, cancellationToken));
@@ -78,7 +79,7 @@ namespace Emby.Server.Implementations.Notifications
return Task.WhenAll(tasks);
}
- private IEnumerable<string> GetUserIds(NotificationRequest request, NotificationOption options)
+ private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption options)
{
if (request.SendToUserMode.HasValue)
{
@@ -86,9 +87,9 @@ namespace Emby.Server.Implementations.Notifications
{
case SendToUserType.Admins:
return _userManager.Users.Where(i => i.Policy.IsAdministrator)
- .Select(i => i.Id.ToString("N"));
+ .Select(i => i.Id);
case SendToUserType.All:
- return _userManager.Users.Select(i => i.Id.ToString("N"));
+ return _userManager.Users.Select(i => i.Id);
case SendToUserType.Custom:
return request.UserIds;
default:
@@ -96,13 +97,13 @@ namespace Emby.Server.Implementations.Notifications
}
}
- if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType))
+ if (options != null && !string.IsNullOrEmpty(request.NotificationType))
{
var config = GetConfiguration();
return _userManager.Users
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy))
- .Select(i => i.Id.ToString("N"));
+ .Select(i => i.Id);
}
return request.UserIds;
@@ -137,90 +138,6 @@ namespace Emby.Server.Implementations.Notifications
}
}
- private string GetTitle(NotificationRequest request, NotificationOption options)
- {
- var title = request.Name;
-
- // If empty, grab from options
- if (string.IsNullOrEmpty(title))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- if (options != null)
- {
- title = options.Title;
- }
- }
- }
-
- // If still empty, grab default
- if (string.IsNullOrEmpty(title))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
-
- if (info != null)
- {
- title = info.DefaultTitle;
- }
- }
- }
-
- title = title ?? string.Empty;
-
- foreach (var pair in request.Variables)
- {
- var token = "{" + pair.Key + "}";
-
- title = title.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
- }
-
- return title;
- }
-
- private string GetDescription(NotificationRequest request, NotificationOption options)
- {
- var text = request.Description;
-
- // If empty, grab from options
- if (string.IsNullOrEmpty(text))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- if (options != null)
- {
- text = options.Description;
- }
- }
- }
-
- // If still empty, grab default
- if (string.IsNullOrEmpty(text))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
-
- if (info != null)
- {
- text = info.DefaultDescription;
- }
- }
- }
-
- text = text ?? string.Empty;
-
- foreach (var pair in request.Variables)
- {
- var token = "{" + pair.Key + "}";
-
- text = text.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
- }
-
- return text;
- }
-
private bool IsEnabledForUser(INotificationService service, User user)
{
try
@@ -241,13 +158,6 @@ namespace Emby.Server.Implementations.Notifications
return true;
}
- var configurable = service as IConfigurableNotificationService;
-
- if (configurable != null)
- {
- return configurable.IsEnabled(notificationType);
- }
-
return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
}
@@ -283,15 +193,9 @@ namespace Emby.Server.Implementations.Notifications
return list;
}
- public IEnumerable<NotificationServiceInfo> GetNotificationServices()
+ public IEnumerable<NameIdPair> GetNotificationServices()
{
- return _services.Where(i =>
- {
- var configurable = i as IConfigurableNotificationService;
-
- return configurable == null || !configurable.IsHidden;
-
- }).Select(i => new NotificationServiceInfo
+ return _services.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Name.GetMD5().ToString("N")
diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs
new file mode 100644
index 000000000..64863eb39
--- /dev/null
+++ b/Emby.Notifications/Notifications.cs
@@ -0,0 +1,299 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Notifications;
+using MediaBrowser.Model.Tasks;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Events;
+
+namespace Emby.Notifications
+{
+ /// <summary>
+ /// Creates notifications for various system events
+ /// </summary>
+ public class Notifications : IServerEntryPoint
+ {
+ private readonly IInstallationManager _installationManager;
+ private readonly IUserManager _userManager;
+ private readonly ILogger _logger;
+
+ private readonly ITaskManager _taskManager;
+ private readonly INotificationManager _notificationManager;
+
+ private readonly ILibraryManager _libraryManager;
+ private readonly ISessionManager _sessionManager;
+ private readonly IServerApplicationHost _appHost;
+ private readonly ITimerFactory _timerFactory;
+
+ private ITimer LibraryUpdateTimer { get; set; }
+ private readonly object _libraryChangedSyncLock = new object();
+
+ private readonly IConfigurationManager _config;
+ private readonly IDeviceManager _deviceManager;
+ private readonly ILocalizationManager _localization;
+ private readonly IActivityManager _activityManager;
+
+ private string[] _coreNotificationTypes;
+
+ public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory)
+ {
+ _installationManager = installationManager;
+ _userManager = userManager;
+ _logger = logger;
+ _taskManager = taskManager;
+ _notificationManager = notificationManager;
+ _libraryManager = libraryManager;
+ _sessionManager = sessionManager;
+ _appHost = appHost;
+ _config = config;
+ _deviceManager = deviceManager;
+ _timerFactory = timerFactory;
+ _localization = localization;
+ _activityManager = activityManager;
+
+ _coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
+ }
+
+ public void Run()
+ {
+ _libraryManager.ItemAdded += _libraryManager_ItemAdded;
+ _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
+ _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
+ _activityManager.EntryCreated += _activityManager_EntryCreated;
+ }
+
+ private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
+ {
+ var type = NotificationType.ServerRestartRequired.ToString();
+
+ var notification = new NotificationRequest
+ {
+ NotificationType = type,
+ Name = string.Format(_localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), _appHost.Name)
+ };
+
+ await SendNotification(notification, null).ConfigureAwait(false);
+ }
+
+ private async void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
+ {
+ var entry = e.Argument;
+
+ var type = entry.Type;
+
+ if (string.IsNullOrEmpty(type) || !_coreNotificationTypes.Contains(type, StringComparer.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
+ var userId = e.Argument.UserId;
+
+ if (!userId.Equals(Guid.Empty) && !GetOptions().IsEnabledToMonitorUser(type, userId))
+ {
+ return;
+ }
+
+ var notification = new NotificationRequest
+ {
+ NotificationType = type,
+ Name = entry.Name,
+ Description = entry.Overview
+ };
+
+ await SendNotification(notification, null).ConfigureAwait(false);
+ }
+
+ private NotificationOptions GetOptions()
+ {
+ return _config.GetConfiguration<NotificationOptions>("notifications");
+ }
+
+ async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
+ {
+ // This notification is for users who can't auto-update (aka running as service)
+ if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate)
+ {
+ return;
+ }
+
+ var type = NotificationType.ApplicationUpdateAvailable.ToString();
+
+ var notification = new NotificationRequest
+ {
+ Description = "Please see emby.media for details.",
+ NotificationType = type,
+ Name = _localization.GetLocalizedString("NewVersionIsAvailable")
+ };
+
+ await SendNotification(notification, null).ConfigureAwait(false);
+ }
+
+ private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
+ void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
+ {
+ if (!FilterItem(e.Item))
+ {
+ return;
+ }
+
+ lock (_libraryChangedSyncLock)
+ {
+ if (LibraryUpdateTimer == null)
+ {
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000,
+ Timeout.Infinite);
+ }
+ else
+ {
+ LibraryUpdateTimer.Change(5000, Timeout.Infinite);
+ }
+
+ _itemsAdded.Add(e.Item);
+ }
+ }
+
+ private bool FilterItem(BaseItem item)
+ {
+ if (item.IsFolder)
+ {
+ return false;
+ }
+
+ if (!item.HasPathProtocol)
+ {
+ return false;
+ }
+
+ if (item is IItemByName)
+ {
+ return false;
+ }
+
+ return item.SourceType == SourceType.Library;
+ }
+
+ private async void LibraryUpdateTimerCallback(object state)
+ {
+ List<BaseItem> items;
+
+ lock (_libraryChangedSyncLock)
+ {
+ items = _itemsAdded.ToList();
+ _itemsAdded.Clear();
+ DisposeLibraryUpdateTimer();
+ }
+
+ items = items.Take(10).ToList();
+
+ foreach (var item in items)
+ {
+ var notification = new NotificationRequest
+ {
+ NotificationType = NotificationType.NewLibraryContent.ToString(),
+ Name = string.Format(_localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), GetItemName(item)),
+ Description = item.Overview
+ };
+
+ await SendNotification(notification, item).ConfigureAwait(false);
+ }
+ }
+
+ public static string GetItemName(BaseItem item)
+ {
+ var name = item.Name;
+ var episode = item as Episode;
+ if (episode != null)
+ {
+ if (episode.IndexNumber.HasValue)
+ {
+ name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
+ }
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
+ }
+ }
+
+ var hasSeries = item as IHasSeriesName;
+
+ if (hasSeries != null)
+ {
+ name = hasSeries.SeriesName + " - " + name;
+ }
+
+ var hasAlbumArtist = item as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ var artists = hasAlbumArtist.AlbumArtists;
+
+ if (artists.Length > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+ else
+ {
+ var hasArtist = item as IHasArtist;
+ if (hasArtist != null)
+ {
+ var artists = hasArtist.Artists;
+
+ if (artists.Length > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+ }
+
+ return name;
+ }
+
+ private async Task SendNotification(NotificationRequest notification, BaseItem relatedItem)
+ {
+ try
+ {
+ await _notificationManager.SendNotification(notification, relatedItem, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending notification", ex);
+ }
+ }
+
+ public void Dispose()
+ {
+ DisposeLibraryUpdateTimer();
+
+ _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
+ _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
+ _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
+ _activityManager.EntryCreated -= _activityManager_EntryCreated;
+ }
+
+ private void DisposeLibraryUpdateTimer()
+ {
+ if (LibraryUpdateTimer != null)
+ {
+ LibraryUpdateTimer.Dispose();
+ LibraryUpdateTimer = null;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Properties/AssemblyInfo.cs b/Emby.Notifications/Properties/AssemblyInfo.cs
index 95113e0ed..d35c8b289 100644
--- a/MediaBrowser.ServerApplication/Properties/AssemblyInfo.cs
+++ b/Emby.Notifications/Properties/AssemblyInfo.cs
@@ -1,30 +1,36 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// 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")]
+[assembly: AssemblyTitle("Emby.Notifications")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Emby Server")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyProduct("Emby.Notifications")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
[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
+// 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("a5a1e61f-da85-4fd6-9d6d-bcf2bf09c1f8")]
+[assembly: Guid("4d1d313b-60bb-4e11-acf9-cda6745266ef")]
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
-// \ No newline at end of file
+//
+// 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.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
index 21631aeb5..f16ce6b9c 100644
--- a/Emby.Photos/Emby.Photos.csproj
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -1,68 +1,20 @@
-<?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>{89AB4548-770D-41FD-A891-8DAFF44F452C}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Photos</RootNamespace>
- <AssemblyName>Emby.Photos</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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<Reference Include="TagLib.Portable">
<HintPath>..\ThirdParty\taglib\TagLib.Portable.dll</HintPath>
</Reference>
</ItemGroup>
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="PhotoProvider.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="..\SharedVersion.cs"/>
</ItemGroup>
- <ItemGroup>
- <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>
- </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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/Emby.Photos/Emby.Photos.nuget.targets b/Emby.Photos/Emby.Photos.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/Emby.Photos/Emby.Photos.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/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index 11a7db47d..f1a05607f 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -14,10 +14,11 @@ using TagLib;
using TagLib.IFD;
using TagLib.IFD.Entries;
using TagLib.IFD.Tags;
+using MediaBrowser.Model.MediaInfo;
namespace Emby.Photos
{
- public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider
+ public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
@@ -30,8 +31,22 @@ namespace Emby.Photos
_imageProcessor = imageProcessor;
}
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
+ {
+ if (item.IsFileProtocol)
+ {
+ var file = directoryService.GetFile(item.Path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
// These are causing taglib to hang
- private string[] _includextensions = new string[] { ".jpg", ".jpeg", ".png", ".tiff" };
+ private string[] _includextensions = new string[] { ".jpg", ".jpeg", ".png", ".tiff", ".cr2" };
public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
@@ -108,7 +123,10 @@ namespace Emby.Photos
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
{
- item.Name = image.ImageTag.Title;
+ if (!item.LockedFields.Contains(MetadataFields.Name))
+ {
+ item.Name = image.ImageTag.Title;
+ }
}
var dateTaken = image.ImageTag.DateTime;
@@ -119,7 +137,7 @@ namespace Emby.Photos
item.ProductionYear = dateTaken.Value.Year;
}
- item.Genres = image.ImageTag.Genres.ToList();
+ item.Genres = image.ImageTag.Genres;
item.Tags = image.ImageTag.Keywords;
item.Software = image.ImageTag.Software;
@@ -161,15 +179,23 @@ namespace Emby.Photos
}
}
- if (!item.Width.HasValue || !item.Height.HasValue)
+ if (item.Width <= 0 || item.Height <= 0)
{
var img = item.GetImageInfo(ImageType.Primary, 0);
- var size = _imageProcessor.GetImageSize(item, img, false, false);
- if (size.Width > 0 && size.Height > 0)
+ try
+ {
+ var size = _imageProcessor.GetImageSize(item, img, false, false);
+
+ if (size.Width > 0 && size.Height > 0)
+ {
+ item.Width = Convert.ToInt32(size.Width);
+ item.Height = Convert.ToInt32(size.Height);
+ }
+ }
+ catch (ArgumentException)
{
- item.Width = Convert.ToInt32(size.Width);
- item.Height = Convert.ToInt32(size.Height);
+ // format not supported
}
}
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 4e448ac64..079d0af0a 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -19,6 +19,11 @@ using System.Linq;
using System.Text;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Notifications;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Authentication;
namespace Emby.Server.Implementations.Activity
{
@@ -26,7 +31,6 @@ namespace Emby.Server.Implementations.Activity
{
private readonly IInstallationManager _installationManager;
- //private readonly ILogManager _logManager;
//private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
private readonly ITaskManager _taskManager;
@@ -38,10 +42,10 @@ namespace Emby.Server.Implementations.Activity
private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
+ private readonly IDeviceManager _deviceManager;
- public ActivityLogEntryPoint(ISessionManager sessionManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost)
+ public ActivityLogEntryPoint(ISessionManager sessionManager, IDeviceManager deviceManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost)
{
- //_logger = _logManager.GetLogger("ActivityLogEntryPoint");
_sessionManager = sessionManager;
_taskManager = taskManager;
_activityManager = activityManager;
@@ -51,21 +55,18 @@ namespace Emby.Server.Implementations.Activity
_subManager = subManager;
_userManager = userManager;
_config = config;
- //_logManager = logManager;
_appHost = appHost;
+ _deviceManager = deviceManager;
}
public void Run()
{
- //_taskManager.TaskExecuting += _taskManager_TaskExecuting;
- //_taskManager.TaskCompleted += _taskManager_TaskCompleted;
+ _taskManager.TaskCompleted += _taskManager_TaskCompleted;
- //_installationManager.PluginInstalled += _installationManager_PluginInstalled;
- //_installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
- //_installationManager.PluginUpdated += _installationManager_PluginUpdated;
-
- //_libraryManager.ItemAdded += _libraryManager_ItemAdded;
- //_libraryManager.ItemRemoved += _libraryManager_ItemRemoved;
+ _installationManager.PluginInstalled += _installationManager_PluginInstalled;
+ _installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
+ _installationManager.PluginUpdated += _installationManager_PluginUpdated;
+ _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
_sessionManager.SessionStarted += _sessionManager_SessionStarted;
_sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed;
@@ -81,24 +82,33 @@ namespace Emby.Server.Implementations.Activity
_userManager.UserCreated += _userManager_UserCreated;
_userManager.UserPasswordChanged += _userManager_UserPasswordChanged;
_userManager.UserDeleted += _userManager_UserDeleted;
- _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
+ _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
_userManager.UserLockedOut += _userManager_UserLockedOut;
//_config.ConfigurationUpdated += _config_ConfigurationUpdated;
//_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
- //_logManager.LoggerLoaded += _logManager_LoggerLoaded;
+ _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
}
+ void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
+ {
+ CreateLogEntry(new ActivityLogEntry
+ {
+ Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name),
+ Type = NotificationType.CameraImageUploaded.ToString()
+ });
+ }
+
void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
- Type = "UserLockedOut",
- UserId = e.Argument.Id.ToString("N")
+ Type = NotificationType.UserLockedOut.ToString(),
+ UserId = e.Argument.Id
});
}
@@ -106,11 +116,10 @@ namespace Emby.Server.Implementations.Activity
{
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureForItem"), Notifications.Notifications.GetItemName(e.Item)),
+ Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
Type = "SubtitleDownloadFailure",
ItemId = e.Item.Id.ToString("N"),
- ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider),
- Overview = LogHelper.GetLogMessage(e.Exception).ToString()
+ ShortOverview = e.Exception.Message
});
}
@@ -139,10 +148,9 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)),
- Type = "PlaybackStopped",
- ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
- UserId = user.Id.ToString("N")
+ Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
+ Type = GetPlaybackStoppedNotificationType(item.MediaType),
+ UserId = user.Id
});
}
@@ -171,19 +179,71 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)),
- Type = "PlaybackStart",
- ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
- UserId = user.Id.ToString("N")
+ Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
+ Type = GetPlaybackNotificationType(item.MediaType),
+ UserId = user.Id
});
}
+ private static string GetItemName(BaseItemDto item)
+ {
+ var name = item.Name;
+
+ if (!string.IsNullOrEmpty(item.SeriesName))
+ {
+ name = item.SeriesName + " - " + name;
+ }
+
+ if (item.Artists != null && item.Artists.Length > 0)
+ {
+ name = item.Artists[0] + " - " + name;
+ }
+
+ return name;
+ }
+
+ private string GetPlaybackNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlayback.ToString();
+ }
+ if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.GamePlayback.ToString();
+ }
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlayback.ToString();
+ }
+
+ return null;
+ }
+
+ private string GetPlaybackStoppedNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlaybackStopped.ToString();
+ }
+ if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.GamePlaybackStopped.ToString();
+ }
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlaybackStopped.ToString();
+ }
+
+ return null;
+ }
+
void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
{
string name;
var session = e.SessionInfo;
- if (string.IsNullOrWhiteSpace(session.UserName))
+ if (string.IsNullOrEmpty(session.UserName))
{
name = string.Format(_localization.GetLocalizedString("DeviceOfflineWithName"), session.DeviceName);
@@ -200,17 +260,20 @@ namespace Emby.Server.Implementations.Activity
Name = name,
Type = "SessionEnded",
ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint),
- UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null
+ UserId = session.UserId
});
}
- void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationRequest> e)
+ void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
{
+ var user = e.Argument.User;
+
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), e.Argument.Username),
+ Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name),
Type = "AuthenticationSucceeded",
- ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint)
+ ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.SessionInfo.RemoteEndPoint),
+ UserId = user.Id
});
}
@@ -229,9 +292,8 @@ namespace Emby.Server.Implementations.Activity
{
CreateLogEntry(new ActivityLogEntry
{
- Name = _localization.GetLocalizedString("MessageApplicationUpdated"),
- Type = "ApplicationUpdated",
- ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr),
+ Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr),
+ Type = NotificationType.ApplicationUpdateInstalled.ToString(),
Overview = e.Argument.description
});
}
@@ -254,13 +316,13 @@ namespace Emby.Server.Implementations.Activity
});
}
- void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
+ void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("UserConfigurationUpdatedWithName"), e.Argument.Name),
- Type = "UserConfigurationUpdated",
- UserId = e.Argument.Id.ToString("N")
+ Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name),
+ Type = "UserPolicyUpdated",
+ UserId = e.Argument.Id
});
}
@@ -279,7 +341,7 @@ namespace Emby.Server.Implementations.Activity
{
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
Type = "UserPasswordChanged",
- UserId = e.Argument.Id.ToString("N")
+ UserId = e.Argument.Id
});
}
@@ -289,7 +351,7 @@ namespace Emby.Server.Implementations.Activity
{
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
Type = "UserCreated",
- UserId = e.Argument.Id.ToString("N")
+ UserId = e.Argument.Id
});
}
@@ -309,7 +371,7 @@ namespace Emby.Server.Implementations.Activity
string name;
var session = e.SessionInfo;
- if (string.IsNullOrWhiteSpace(session.UserName))
+ if (string.IsNullOrEmpty(session.UserName))
{
name = string.Format(_localization.GetLocalizedString("DeviceOnlineWithName"), session.DeviceName);
@@ -326,36 +388,7 @@ namespace Emby.Server.Implementations.Activity
Name = name,
Type = "SessionStarted",
ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint),
- UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null
- });
- }
-
- void _libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
- {
- if (e.Item.SourceType != SourceType.Library)
- {
- return;
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ItemRemovedWithName"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "ItemRemoved"
- });
- }
-
- void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (e.Item.SourceType != SourceType.Library)
- {
- return;
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ItemAddedWithName"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "ItemAdded",
- ItemId = e.Item.Id.ToString("N")
+ UserId = session.UserId
});
}
@@ -364,7 +397,7 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
- Type = "PluginUpdated",
+ Type = NotificationType.PluginUpdateInstalled.ToString(),
ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.versionStr),
Overview = e.Argument.Item2.description
});
@@ -375,7 +408,7 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
- Type = "PluginUninstalled"
+ Type = NotificationType.PluginUninstalled.ToString()
});
}
@@ -384,25 +417,21 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
- Type = "PluginInstalled",
+ Type = NotificationType.PluginInstalled.ToString(),
ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr)
});
}
- void _taskManager_TaskExecuting(object sender, GenericEventArgs<IScheduledTaskWorker> e)
+ void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
- var task = e.Argument;
-
- var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
- if (activityTask != null && !activityTask.IsLogged)
- {
- return;
- }
+ var installationInfo = e.InstallationInfo;
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("ScheduledTaskStartedWithName"), task.Name),
- Type = "ScheduledTaskStarted"
+ Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name),
+ Type = NotificationType.InstallationFailed.ToString(),
+ ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), installationInfo.Version),
+ Overview = e.Exception.Message
});
}
@@ -424,11 +453,11 @@ namespace Emby.Server.Implementations.Activity
{
var vals = new List<string>();
- if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage))
+ if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
{
vals.Add(e.Result.ErrorMessage);
}
- if (!string.IsNullOrWhiteSpace(e.Result.LongErrorMessage))
+ if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
{
vals.Add(e.Result.LongErrorMessage);
}
@@ -436,7 +465,7 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
- Type = "ScheduledTaskFailed",
+ Type = NotificationType.TaskFailed.ToString(),
Overview = string.Join(Environment.NewLine, vals.ToArray(vals.Count)),
ShortOverview = runningTime,
Severity = LogSeverity.Error
@@ -458,15 +487,12 @@ namespace Emby.Server.Implementations.Activity
public void Dispose()
{
- _taskManager.TaskExecuting -= _taskManager_TaskExecuting;
_taskManager.TaskCompleted -= _taskManager_TaskCompleted;
_installationManager.PluginInstalled -= _installationManager_PluginInstalled;
_installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
_installationManager.PluginUpdated -= _installationManager_PluginUpdated;
-
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- _libraryManager.ItemRemoved -= _libraryManager_ItemRemoved;
+ _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
_sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed;
@@ -482,16 +508,15 @@ namespace Emby.Server.Implementations.Activity
_userManager.UserCreated -= _userManager_UserCreated;
_userManager.UserPasswordChanged -= _userManager_UserPasswordChanged;
_userManager.UserDeleted -= _userManager_UserDeleted;
- _userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
+ _userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated;
_userManager.UserLockedOut -= _userManager_UserLockedOut;
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated;
- //_logManager.LoggerLoaded -= _logManager_LoggerLoaded;
+ _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
- GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index 9a3f1ae47..047bebf23 100644
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using System;
using System.Linq;
-using System.Threading.Tasks;
namespace Emby.Server.Implementations.Activity
{
@@ -27,7 +26,6 @@ namespace Emby.Server.Implementations.Activity
public void Create(ActivityLogEntry entry)
{
- entry.Id = Guid.NewGuid().ToString("N");
entry.Date = DateTime.UtcNow;
_repo.Create(entry);
@@ -35,11 +33,11 @@ namespace Emby.Server.Implementations.Activity
EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger);
}
- public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
+ public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
{
- var result = _repo.GetActivityLogEntries(minDate, startIndex, limit);
+ var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
- foreach (var item in result.Items.Where(i => !string.IsNullOrWhiteSpace(i.UserId)))
+ foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty)))
{
var user = _userManager.GetUserById(item.UserId);
@@ -52,5 +50,10 @@ namespace Emby.Server.Implementations.Activity
return result;
}
+
+ public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
+ {
+ return GetActivityLogEntries(minDate, null, startIndex, limit);
+ }
}
}
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 6293cc69f..ce9f460ff 100644
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -48,20 +48,76 @@ namespace Emby.Server.Implementations.Activity
{
RunDefaultInitialization(connection);
- string[] queries = {
- "create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY NOT NULL, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
- "create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)"
- };
+ connection.RunQueries(new[]
+ {
+ "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
+ "drop index if exists idx_ActivityLogEntries"
+ });
- connection.RunQueries(queries);
+ TryMigrate(connection);
}
}
- private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
+ private void TryMigrate(ManagedConnection connection)
+ {
+ try
+ {
+ if (TableExists(connection, "ActivityLogEntries"))
+ {
+ connection.RunQueries(new[]
+ {
+ "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
+ "drop table if exists ActivityLogEntries"
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error migrating activity log database", ex);
+ }
+ }
+
+ private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
public void Create(ActivityLogEntry entry)
{
- Update(entry);
+ if (entry == null)
+ {
+ throw new ArgumentNullException("entry");
+ }
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
+ {
+ statement.TryBind("@Name", entry.Name);
+
+ statement.TryBind("@Overview", entry.Overview);
+ statement.TryBind("@ShortOverview", entry.ShortOverview);
+ statement.TryBind("@Type", entry.Type);
+ statement.TryBind("@ItemId", entry.ItemId);
+
+ if (entry.UserId.Equals(Guid.Empty))
+ {
+ statement.TryBindNull("@UserId");
+ }
+ else
+ {
+ statement.TryBind("@UserId", entry.UserId.ToString("N"));
+ }
+
+ statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
+ statement.TryBind("@LogSeverity", entry.Severity.ToString());
+
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
}
public void Update(ActivityLogEntry entry)
@@ -77,16 +133,25 @@ namespace Emby.Server.Implementations.Activity
{
connection.RunInTransaction(db =>
{
- using (var statement = db.PrepareStatement("replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
+ using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
{
- statement.TryBind("@Id", entry.Id.ToGuidBlob());
- statement.TryBind("@Name", entry.Name);
+ statement.TryBind("@Id", entry.Id);
+ statement.TryBind("@Name", entry.Name);
statement.TryBind("@Overview", entry.Overview);
statement.TryBind("@ShortOverview", entry.ShortOverview);
statement.TryBind("@Type", entry.Type);
statement.TryBind("@ItemId", entry.ItemId);
- statement.TryBind("@UserId", entry.UserId);
+
+ if (entry.UserId.Equals(Guid.Empty))
+ {
+ statement.TryBindNull("@UserId");
+ }
+ else
+ {
+ statement.TryBind("@UserId", entry.UserId.ToString("N"));
+ }
+
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
statement.TryBind("@LogSeverity", entry.Severity.ToString());
@@ -97,7 +162,7 @@ namespace Emby.Server.Implementations.Activity
}
}
- public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
+ public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
{
using (WriteLock.Read())
{
@@ -110,6 +175,17 @@ namespace Emby.Server.Implementations.Activity
{
whereClauses.Add("DateCreated>=@DateCreated");
}
+ if (hasUserId.HasValue)
+ {
+ if (hasUserId.Value)
+ {
+ whereClauses.Add("UserId not null");
+ }
+ else
+ {
+ whereClauses.Add("UserId is null");
+ }
+ }
var whereTextWithoutPaging = whereClauses.Count == 0 ?
string.Empty :
@@ -121,7 +197,7 @@ namespace Emby.Server.Implementations.Activity
string.Empty :
" 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})",
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
pagingWhereText,
startIndex.Value.ToString(_usCulture)));
}
@@ -141,7 +217,7 @@ namespace Emby.Server.Implementations.Activity
var statementTexts = new List<string>();
statementTexts.Add(commandText);
- statementTexts.Add("select count (Id) from ActivityLogEntries" + whereTextWithoutPaging);
+ statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging);
return connection.RunInTransaction(db =>
{
@@ -187,7 +263,7 @@ namespace Emby.Server.Implementations.Activity
var info = new ActivityLogEntry
{
- Id = reader[index].ReadGuidFromBlob().ToString("N")
+ Id = reader[index].ToInt64()
};
index++;
@@ -223,7 +299,7 @@ namespace Emby.Server.Implementations.Activity
index++;
if (reader[index].SQLiteType != SQLiteType.Null)
{
- info.UserId = reader[index].ToString();
+ info.UserId = new Guid(reader[index].ToString());
}
index++;
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index 1e63aa1a6..52e421374 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -49,6 +49,15 @@ namespace Emby.Server.Implementations.AppBase
}
}
+ private const string _virtualDataPath = "%AppDataPath%";
+ public string VirtualDataPath
+ {
+ get
+ {
+ return _virtualDataPath;
+ }
+ }
+
/// <summary>
/// Gets the image cache path.
/// </summary>
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 26450c06c..3208c6a1f 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1,12 +1,9 @@
using Emby.Common.Implementations.Serialization;
+using Emby.Drawing;
+using Emby.Photos;
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.Archiving;
using Emby.Server.Implementations.Channels;
@@ -26,14 +23,13 @@ using Emby.Server.Implementations.LiveTv;
using Emby.Server.Implementations.Localization;
using Emby.Server.Implementations.MediaEncoder;
using Emby.Server.Implementations.Net;
-using Emby.Server.Implementations.Notifications;
+using Emby.Notifications;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.Reflection;
using Emby.Server.Implementations.ScheduledTasks;
using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization;
using Emby.Server.Implementations.Session;
-using Emby.Server.Implementations.Social;
using Emby.Server.Implementations.Threading;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
@@ -46,7 +42,7 @@ using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
@@ -60,6 +56,7 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -74,7 +71,6 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Controller.Sync;
using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
@@ -93,7 +89,6 @@ 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.Tasks;
using MediaBrowser.Model.Text;
@@ -105,7 +100,6 @@ using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
-using OpenSubtitlesHandler;
using ServiceStack;
using System;
using System.Collections.Concurrent;
@@ -122,13 +116,16 @@ using System.Threading;
using System.Threading.Tasks;
using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
+using MediaBrowser.Controller.Authentication;
+using System.Diagnostics;
+using ServiceStack.Text.Jsv;
namespace Emby.Server.Implementations
{
/// <summary>
/// Class CompositionRoot
/// </summary>
- public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer, IDisposable
+ public abstract class ApplicationHost : IServerApplicationHost, IDisposable
{
/// <summary>
/// Gets a value indicating whether this instance can self restart.
@@ -219,16 +216,10 @@ namespace Emby.Server.Implementations
protected ServerApplicationPaths ApplicationPaths { get; set; }
/// <summary>
- /// Gets assemblies that failed to load
- /// </summary>
- /// <value>The failed assemblies.</value>
- public List<string> FailedAssemblies { get; protected set; }
-
- /// <summary>
/// Gets all concrete types.
/// </summary>
/// <value>All concrete types.</value>
- public Type[] AllConcreteTypes { get; protected set; }
+ public Tuple<Type, string>[] AllConcreteTypes { get; protected set; }
/// <summary>
/// The disposable parts
@@ -236,12 +227,6 @@ namespace Emby.Server.Implementations
protected readonly List<IDisposable> DisposableParts = new List<IDisposable>();
/// <summary>
- /// Gets a value indicating whether this instance is first run.
- /// </summary>
- /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
- public bool IsFirstRun { get; private set; }
-
- /// <summary>
/// Gets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
@@ -276,7 +261,6 @@ namespace Emby.Server.Implementations
protected readonly SimpleInjector.Container Container = new SimpleInjector.Container();
protected ISystemEvents SystemEvents { get; set; }
- protected IMemoryStreamFactory MemoryStreamFactory { get; set; }
/// <summary>
/// Gets the server configuration manager.
@@ -296,11 +280,11 @@ namespace Emby.Server.Implementations
return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager);
}
- /// <summary>
- /// Gets or sets the server manager.
- /// </summary>
- /// <value>The server manager.</value>
- private IServerManager ServerManager { get; set; }
+ protected virtual IResourceFileManager CreateResourceFileManager()
+ {
+ return new ResourceFileManager(HttpResultFactory, LogManager.GetLogger("ResourceManager"), FileSystemManager);
+ }
+
/// <summary>
/// Gets or sets the user manager.
/// </summary>
@@ -345,7 +329,7 @@ namespace Emby.Server.Implementations
private IEncodingManager EncodingManager { get; set; }
private IChannelManager ChannelManager { get; set; }
- private ISyncManager SyncManager { get; set; }
+ protected ITextEncoding TextEncoding { get; private set; }
/// <summary>
/// Gets or sets the user data repository.
@@ -355,7 +339,6 @@ namespace Emby.Server.Implementations
private IUserRepository UserRepository { get; set; }
internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; }
internal IItemRepository ItemRepository { get; set; }
- private INotificationsRepository NotificationsRepository { get; set; }
private INotificationManager NotificationManager { get; set; }
private ISubtitleManager SubtitleManager { get; set; }
@@ -386,7 +369,7 @@ namespace Emby.Server.Implementations
/// </summary>
/// <value>The zip client.</value>
protected IZipClient ZipClient { get; private set; }
-
+ protected IHttpResultFactory HttpResultFactory { get; private set; }
protected IAuthService AuthService { get; private set; }
public StartupOptions StartupOptions { get; private set; }
@@ -428,11 +411,9 @@ namespace Emby.Server.Implementations
XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer"));
NetworkManager = networkManager;
+ networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
EnvironmentInfo = environmentInfo;
SystemEvents = systemEvents;
- MemoryStreamFactory = new MemoryStreamProvider();
-
- FailedAssemblies = new List<string>();
ApplicationPaths = applicationPaths;
LogManager = logManager;
@@ -456,6 +437,27 @@ namespace Emby.Server.Implementations
NetworkManager.NetworkChanged += NetworkManager_NetworkChanged;
}
+ public string ExpandVirtualPath(string path)
+ {
+ var appPaths = ApplicationPaths;
+
+ return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase)
+ .Replace(appPaths.VirtualInternalMetadataPath, appPaths.InternalMetadataPath, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public string ReverseVirtualPath(string path)
+ {
+ var appPaths = ApplicationPaths;
+
+ return path.Replace(appPaths.DataPath, appPaths.VirtualDataPath, StringComparison.OrdinalIgnoreCase)
+ .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private string[] GetConfiguredLocalSubnets()
+ {
+ return ServerConfigurationManager.Configuration.LocalNetworkSubnets;
+ }
+
private void NetworkManager_NetworkChanged(object sender, EventArgs e)
{
_validAddressResults.Clear();
@@ -470,7 +472,7 @@ namespace Emby.Server.Implementations
{
get
{
- return _version ?? (_version = GetAssembly(GetType()).GetName().Version);
+ return _version ?? (_version = GetType().GetTypeInfo().Assembly.GetName().Version);
}
}
@@ -500,9 +502,17 @@ namespace Emby.Server.Implementations
}
}
- private Assembly GetAssembly(Type type)
+ private Tuple<Assembly, string> GetAssembly(Type type)
{
- return type.GetTypeInfo().Assembly;
+ var assembly = type.GetTypeInfo().Assembly;
+ string path = null;
+
+ return new Tuple<Assembly, string>(assembly, path);
+ }
+
+ public virtual IStreamHelper CreateStreamHelper()
+ {
+ return new StreamHelper();
}
public virtual bool SupportsAutoRunAtStartup
@@ -520,16 +530,7 @@ namespace Emby.Server.Implementations
/// <returns>System.Object.</returns>
public object CreateInstance(Type type)
{
- try
- {
- return Container.GetInstance(type);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error creating {0}", ex, type.FullName);
-
- throw;
- }
+ return Container.GetInstance(type);
}
/// <summary>
@@ -537,8 +538,10 @@ namespace Emby.Server.Implementations
/// </summary>
/// <param name="type">The type.</param>
/// <returns>System.Object.</returns>
- protected object CreateInstanceSafe(Type type)
+ protected object CreateInstanceSafe(Tuple<Type, string> typeInfo)
{
+ var type = typeInfo.Item1;
+
try
{
return Container.GetInstance(type);
@@ -615,15 +618,16 @@ namespace Emby.Server.Implementations
/// </summary>
/// <param name="file">The file.</param>
/// <returns>Assembly.</returns>
- protected Assembly LoadAssembly(string file)
+ protected Tuple<Assembly, string> LoadAssembly(string file)
{
try
{
- return Assembly.Load(File.ReadAllBytes(file));
+ var assembly = Assembly.Load(File.ReadAllBytes(file));
+
+ return new Tuple<Assembly, string>(assembly, file);
}
catch (Exception ex)
{
- FailedAssemblies.Add(file);
Logger.ErrorException("Error loading assembly {0}", ex, file);
return null;
}
@@ -634,11 +638,11 @@ namespace Emby.Server.Implementations
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IEnumerable{Type}.</returns>
- public IEnumerable<Type> GetExportTypes<T>()
+ public IEnumerable<Tuple<Type, string>> GetExportTypes<T>()
{
var currentType = typeof(T);
- return AllConcreteTypes.Where(currentType.IsAssignableFrom);
+ return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i.Item1));
}
/// <summary>
@@ -666,6 +670,33 @@ namespace Emby.Server.Implementations
return parts;
}
+ public List<Tuple<T, string>> GetExportsWithInfo<T>(bool manageLiftime = true)
+ {
+ var parts = GetExportTypes<T>()
+ .Select(i =>
+ {
+ var obj = CreateInstanceSafe(i);
+
+ if (obj == null)
+ {
+ return null;
+ }
+ return new Tuple<T, string>((T)obj, i.Item2);
+ })
+ .Where(i => i != null)
+ .ToList();
+
+ if (manageLiftime)
+ {
+ lock (DisposableParts)
+ {
+ DisposableParts.AddRange(parts.Select(i => i.Item1).OfType<IDisposable>());
+ }
+ }
+
+ return parts;
+ }
+
private void SetBaseExceptionMessage()
{
var builder = GetBaseExceptionMessage(ApplicationPaths);
@@ -687,25 +718,42 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
- await MediaEncoder.Init().ConfigureAwait(false);
+ MediaEncoder.Init();
- if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
- {
- if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
- {
- ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false;
- ServerConfigurationManager.SaveConfiguration();
- }
- }
+ //if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
+ //{
+ // if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
+ // {
+ // ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false;
+ // ServerConfigurationManager.SaveConfiguration();
+ // }
+ //}
Logger.Info("ServerId: {0}", SystemId);
+
+ var entryPoints = GetExports<IServerEntryPoint>().ToList();
+ RunEntryPoints(entryPoints, true);
+
Logger.Info("Core startup complete");
HttpServer.GlobalResponse = null;
Logger.Info("Post-init migrations complete");
- foreach (var entryPoint in GetExports<IServerEntryPoint>().ToList())
+ RunEntryPoints(entryPoints, false);
+ Logger.Info("All entry points have started");
+
+ LogManager.RemoveConsoleOutput();
+ }
+
+ private void RunEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
+ {
+ foreach (var entryPoint in entryPoints)
{
+ if (isBeforeStartup != (entryPoint is IRunBeforeStartup))
+ {
+ continue;
+ }
+
var name = entryPoint.GetType().FullName;
Logger.Info("Starting entry point {0}", name);
var now = DateTime.UtcNow;
@@ -719,9 +767,6 @@ namespace Emby.Server.Implementations
}
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");
-
- LogManager.RemoveConsoleOutput();
}
/// <summary>
@@ -741,20 +786,10 @@ namespace Emby.Server.Implementations
private IJsonSerializer CreateJsonSerializer()
{
- try
- {
- // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
- Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
- }
- catch
- {
- // Failing under mono
- }
-
return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer"));
}
- public async Task Init(IProgress<double> progress)
+ public void Init()
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -766,39 +801,22 @@ namespace Emby.Server.Implementations
HttpsPort = ServerConfiguration.DefaultHttpsPort;
}
- progress.Report(1);
-
JsonSerializer = CreateJsonSerializer();
OnLoggerLoaded(true);
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
- IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted;
- progress.Report(2);
-
LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
? LogSeverity.Debug
: LogSeverity.Info;
- progress.Report(3);
-
DiscoverTypes();
- progress.Report(14);
SetHttpLimit();
- progress.Report(15);
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(.8 * p + 15));
- await RegisterResources(innerProgress).ConfigureAwait(false);
+ RegisterResources();
FindParts();
- progress.Report(95);
-
- await InstallIsoMounters(CancellationToken.None).ConfigureAwait(false);
-
- progress.Report(100);
}
protected virtual void OnLoggerLoaded(bool isFirstLoad)
@@ -810,16 +828,6 @@ namespace Emby.Server.Implementations
LogEnvironmentInfo(Logger, ApplicationPaths, false);
}
- // Put the app config in the log for troubleshooting purposes
- var configJson = new StringBuilder(JsonSerializer.SerializeToString(ConfigurationManager.CommonConfiguration));
-
- if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePassword))
- {
- configJson = configJson.Replace(ServerConfigurationManager.Configuration.CertificatePassword, "####");
- }
-
- Logger.LogMultiline("Application configuration:", LogSeverity.Info, configJson);
-
if (Plugins != null)
{
var pluginBuilder = new StringBuilder();
@@ -834,17 +842,18 @@ namespace Emby.Server.Implementations
}
protected abstract IConnectManager CreateConnectManager();
- protected abstract ISyncManager CreateSyncManager();
protected virtual IHttpClient CreateHttpClient()
{
- return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent);
+ return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, GetDefaultUserAgent);
}
+ public static IStreamHelper StreamHelper { get; set; }
+
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
- protected async Task RegisterResources(IProgress<double> progress)
+ protected void RegisterResources()
{
RegisterSingleInstance(ConfigurationManager);
RegisterSingleInstance<IApplicationHost>(this);
@@ -852,7 +861,6 @@ namespace Emby.Server.Implementations
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
RegisterSingleInstance(JsonSerializer);
- RegisterSingleInstance(MemoryStreamFactory);
RegisterSingleInstance(SystemEvents);
RegisterSingleInstance(LogManager, false);
@@ -881,6 +889,10 @@ namespace Emby.Server.Implementations
TimerFactory = new TimerFactory();
RegisterSingleInstance(TimerFactory);
+ var streamHelper = CreateStreamHelper();
+ ApplicationHost.StreamHelper = streamHelper;
+ RegisterSingleInstance(streamHelper);
+
RegisterSingleInstance(CryptographyProvider);
SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory"));
@@ -891,13 +903,14 @@ namespace Emby.Server.Implementations
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider);
RegisterSingleInstance(SecurityManager);
- InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
+ InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
RegisterSingleInstance(InstallationManager);
ZipClient = new ZipClient(FileSystemManager);
RegisterSingleInstance(ZipClient);
- RegisterSingleInstance<IHttpResultFactory>(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, MemoryStreamFactory));
+ HttpResultFactory = new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, CreateBrotliCompressor());
+ RegisterSingleInstance(HttpResultFactory);
RegisterSingleInstance<IServerApplicationHost>(this);
RegisterSingleInstance<IServerApplicationPaths>(ApplicationPaths);
@@ -911,26 +924,25 @@ namespace Emby.Server.Implementations
StringExtensions.LocalizationManager = LocalizationManager;
RegisterSingleInstance(LocalizationManager);
- ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer);
- RegisterSingleInstance(textEncoding);
- Utilities.EncodingHelper = textEncoding;
- BlurayExaminer = new BdInfoExaminer(FileSystemManager, textEncoding);
+ TextEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer);
+ RegisterSingleInstance(TextEncoding);
+ BlurayExaminer = new BdInfoExaminer(FileSystemManager, TextEncoding);
RegisterSingleInstance(BlurayExaminer);
RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
- UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager);
+ UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager, () => UserManager);
RegisterSingleInstance(UserDataManager);
UserRepository = GetUserRepository();
// This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
RegisterSingleInstance(UserRepository);
- var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory, FileSystemManager);
+ var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, FileSystemManager);
DisplayPreferencesRepository = displayPreferencesRepo;
RegisterSingleInstance(DisplayPreferencesRepository);
- var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory);
+ var itemRepo = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory);
ItemRepository = itemRepo;
RegisterSingleInstance(ItemRepository);
@@ -940,7 +952,7 @@ namespace Emby.Server.Implementations
UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider);
RegisterSingleInstance(UserManager);
- LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
+ LibraryManager = new LibraryManager(this, Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
RegisterSingleInstance(LibraryManager);
var musicManager = new MusicManager(LibraryManager);
@@ -949,24 +961,23 @@ namespace Emby.Server.Implementations
LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, TimerFactory, SystemEvents, EnvironmentInfo);
RegisterSingleInstance(LibraryMonitor);
- ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer, MemoryStreamFactory);
- RegisterSingleInstance(ProviderManager);
-
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
- HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets);
- HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
- RegisterSingleInstance(HttpServer, false);
- progress.Report(10);
-
- ServerManager = new ServerManager.ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding);
- RegisterSingleInstance(ServerManager);
+ HttpServer = new HttpListenerHost(this,
+ LogManager.GetLogger("HttpServer"),
+ ServerConfigurationManager,
+ "web/index.html",
+ NetworkManager,
+ TextEncoding,
+ JsonSerializer,
+ XmlSerializer,
+ GetParseFn);
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
+ HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
+ RegisterSingleInstance(HttpServer);
ImageProcessor = GetImageProcessor();
RegisterSingleInstance(ImageProcessor);
@@ -974,112 +985,101 @@ namespace Emby.Server.Implementations
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
RegisterSingleInstance(TVSeriesManager);
- SyncManager = CreateSyncManager();
- RegisterSingleInstance(SyncManager);
-
- DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
- RegisterSingleInstance(DtoService);
-
var encryptionManager = new EncryptionManager();
RegisterSingleInstance<IEncryptionManager>(encryptionManager);
ConnectManager = CreateConnectManager();
RegisterSingleInstance(ConnectManager);
- var deviceRepo = new SqliteDeviceRepository(LogManager.GetLogger("DeviceManager"), ServerConfigurationManager, FileSystemManager, JsonSerializer);
- deviceRepo.Initialize();
- DeviceManager = new DeviceManager(deviceRepo, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager);
- RegisterSingleInstance<IDeviceRepository>(deviceRepo);
+ DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager);
RegisterSingleInstance(DeviceManager);
var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
RegisterSingleInstance<INewsService>(newsService);
- progress.Report(15);
+ MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager, TimerFactory, () => MediaEncoder);
+ RegisterSingleInstance(MediaSourceManager);
+
+ SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, MediaSourceManager, ServerConfigurationManager, LocalizationManager);
+ RegisterSingleInstance(SubtitleManager);
+
+ ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
+ RegisterSingleInstance(ProviderManager);
+
+ DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
+ RegisterSingleInstance(DtoService);
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
RegisterSingleInstance(ChannelManager);
- MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager, TimerFactory);
- RegisterSingleInstance(MediaSourceManager);
-
SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory);
RegisterSingleInstance(SessionManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this, assemblyInfo);
RegisterSingleInstance<IDlnaManager>(dlnaManager);
- var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient, new XmlReaderSettingsFactory());
- RegisterSingleInstance<IConnectionManager>(connectionManager);
-
- CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager);
+ CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager);
RegisterSingleInstance(CollectionManager);
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager);
RegisterSingleInstance<IPlaylistManager>(PlaylistManager);
- LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager);
+ LiveTvManager = new LiveTvManager(this, HttpClient, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager, () => ChannelManager);
RegisterSingleInstance(LiveTvManager);
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(), TVSeriesManager);
- RegisterSingleInstance<IContentDirectory>(contentDirectory);
-
- var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager, new XmlReaderSettingsFactory());
- RegisterSingleInstance<IMediaReceiverRegistrar>(mediaRegistrar);
-
NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager);
RegisterSingleInstance(NotificationManager);
- SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager, ServerConfigurationManager);
- RegisterSingleInstance(SubtitleManager);
-
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory));
ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
RegisterSingleInstance(ChapterManager);
- await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
- progress.Report(90);
+ RegisterMediaEncoder(assemblyInfo);
EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager);
RegisterSingleInstance(EncodingManager);
- var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths, FileSystemManager);
- sharingRepo.Initialize();
- // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
- RegisterSingleInstance<ISharingRepository>(sharingRepo);
- RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this));
-
var activityLogRepo = GetActivityLogRepository();
RegisterSingleInstance(activityLogRepo);
RegisterSingleInstance<IActivityManager>(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo, UserManager));
- var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager);
+ var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager, UserManager);
RegisterSingleInstance<IAuthorizationContext>(authContext);
RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
- AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager);
+ AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, NetworkManager);
RegisterSingleInstance<IAuthService>(AuthService);
- SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamFactory, ProcessFactory, textEncoding);
+ SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory, TextEncoding);
RegisterSingleInstance(SubtitleEncoder);
+ RegisterSingleInstance(CreateResourceFileManager());
+
displayPreferencesRepo.Initialize();
var userDataRepo = new SqliteUserDataRepository(LogManager.GetLogger("SqliteUserDataRepository"), ApplicationPaths, FileSystemManager);
+ SetStaticProperties();
+
+ ((UserManager)UserManager).Initialize();
+
((UserDataManager)UserDataManager).Repository = userDataRepo;
- itemRepo.Initialize(userDataRepo);
+ itemRepo.Initialize(userDataRepo, UserManager);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
- ConfigureNotificationsRepository();
- progress.Report(100);
+ }
- SetStaticProperties();
+ protected virtual IBrotliCompressor CreateBrotliCompressor()
+ {
+ return null;
+ }
- ((UserManager)UserManager).Initialize();
+ private static Func<string, object> GetParseFn(Type propertyType)
+ {
+ return s => JsvReader.GetParseFn(propertyType)(s);
}
public virtual string PackageRuntime
@@ -1099,7 +1099,13 @@ namespace Emby.Server.Implementations
{
var builder = new StringBuilder();
- builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs())));
+ // Distinct these to prevent users from reporting problems that aren't actually problems
+ var commandLineArgs = Environment
+ .GetCommandLineArgs()
+ .Distinct()
+ .ToArray();
+
+ builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", commandLineArgs)));
builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion));
builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem));
@@ -1136,37 +1142,6 @@ namespace Emby.Server.Implementations
}
}
- /// <summary>
- /// Installs the iso mounters.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task InstallIsoMounters(CancellationToken cancellationToken)
- {
- var list = new List<IIsoMounter>();
-
- foreach (var isoMounter in GetExports<IIsoMounter>())
- {
- try
- {
- if (isoMounter.RequiresInstallation && !isoMounter.IsInstalled)
- {
- Logger.Info("Installing {0}", isoMounter.Name);
-
- await isoMounter.Install(cancellationToken).ConfigureAwait(false);
- }
-
- list.Add(isoMounter);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("{0} failed to load.", ex, isoMounter.Name);
- }
- }
-
- IsoManager.AddParts(list);
- }
-
protected string GetDefaultUserAgent()
{
var name = FormatAttribute(Name);
@@ -1222,7 +1197,7 @@ namespace Emby.Server.Implementations
//localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
if (!localCert.HasPrivateKey)
{
- //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation);
+ Logger.Error("No private key included in SSL cert {0}.", certificateLocation);
return null;
}
@@ -1255,7 +1230,6 @@ namespace Emby.Server.Implementations
info.FFProbeFilename = "ffprobe";
info.ArchiveType = "7z";
info.Version = "20170308";
- info.DownloadUrls = GetLinuxDownloadUrls();
}
else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
{
@@ -1263,7 +1237,6 @@ namespace Emby.Server.Implementations
info.FFProbeFilename = "ffprobe.exe";
info.Version = "20170308";
info.ArchiveType = "7z";
- info.DownloadUrls = GetWindowsDownloadUrls();
}
else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
{
@@ -1271,80 +1244,27 @@ namespace Emby.Server.Implementations
info.FFProbeFilename = "ffprobe";
info.ArchiveType = "7z";
info.Version = "20170308";
- info.DownloadUrls = GetMacDownloadUrls();
- }
- else
- {
- // No version available - user requirement
- info.DownloadUrls = new string[] { };
}
return info;
}
- private string[] GetMacDownloadUrls()
- {
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case MediaBrowser.Model.System.Architecture.X64:
- return new[]
- {
- "https://embydata.com/downloads/ffmpeg/osx/ffmpeg-x64-20170308.7z"
- };
- }
-
- return new string[] { };
- }
-
- private string[] GetWindowsDownloadUrls()
- {
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case MediaBrowser.Model.System.Architecture.X64:
- return new[]
- {
- "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win64.7z"
- };
- case MediaBrowser.Model.System.Architecture.X86:
- return new[]
- {
- "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win32.7z"
- };
- }
-
- return new string[] { };
- }
-
- private string[] GetLinuxDownloadUrls()
+ protected virtual FFMpegInfo GetFFMpegInfo()
{
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case MediaBrowser.Model.System.Architecture.X64:
- return new[]
- {
- "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-64bit-static.7z"
- };
- case MediaBrowser.Model.System.Architecture.X86:
- return new[]
- {
- "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-32bit-static.7z"
- };
- }
-
- return new string[] { };
+ return new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
+ .GetFFMpegInfo(StartupOptions);
}
/// <summary>
/// Registers the media encoder.
/// </summary>
/// <returns>Task.</returns>
- private async Task RegisterMediaEncoder(IProgress<double> progress)
+ private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo)
{
string encoderPath = null;
string probePath = null;
- var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
- .GetFFMpegInfo(StartupOptions, progress).ConfigureAwait(false);
+ var info = GetFFMpegInfo();
encoderPath = info.EncoderPath;
probePath = info.ProbePath;
@@ -1366,12 +1286,11 @@ namespace Emby.Server.Implementations
() => MediaSourceManager,
HttpClient,
ZipClient,
- MemoryStreamFactory,
ProcessFactory,
- (Environment.ProcessorCount > 2 ? 14000 : 40000),
- EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows,
EnvironmentInfo,
- BlurayExaminer);
+ BlurayExaminer,
+ assemblyInfo,
+ this);
MediaEncoder = mediaEncoder;
RegisterSingleInstance(MediaEncoder);
@@ -1383,7 +1302,7 @@ namespace Emby.Server.Implementations
/// <returns>Task{IUserRepository}.</returns>
private IUserRepository GetUserRepository()
{
- var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer, MemoryStreamFactory);
+ var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer);
repo.Initialize();
@@ -1392,7 +1311,7 @@ namespace Emby.Server.Implementations
private IAuthenticationRepository GetAuthenticationRepository()
{
- var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths);
+ var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager);
repo.Initialize();
@@ -1409,24 +1328,12 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Configures the repositories.
- /// </summary>
- private void ConfigureNotificationsRepository()
- {
- var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
-
- repo.Initialize();
-
- NotificationsRepository = repo;
-
- RegisterSingleInstance(NotificationsRepository);
- }
-
- /// <summary>
/// Dirty hacks
/// </summary>
private void SetStaticProperties()
{
+ ((SqliteItemRepository)ItemRepository).ImageProcessor = ImageProcessor;
+
// For now there's no real way to inject these properly
BaseItem.Logger = LogManager.GetLogger("BaseItem");
BaseItem.ConfigurationManager = ServerConfigurationManager;
@@ -1434,20 +1341,19 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = ProviderManager;
BaseItem.LocalizationManager = LocalizationManager;
BaseItem.ItemRepository = ItemRepository;
- User.XmlSerializer = XmlSerializer;
User.UserManager = UserManager;
- Folder.UserManager = UserManager;
BaseItem.FileSystem = FileSystemManager;
BaseItem.UserDataManager = UserDataManager;
BaseItem.ChannelManager = ChannelManager;
- BaseItem.LiveTvManager = LiveTvManager;
+ Video.LiveTvManager = LiveTvManager;
Folder.UserViewManager = UserViewManager;
UserView.TVSeriesManager = TVSeriesManager;
UserView.PlaylistManager = PlaylistManager;
- BaseItem.CollectionManager = CollectionManager;
+ UserView.CollectionManager = CollectionManager;
BaseItem.MediaSourceManager = MediaSourceManager;
CollectionFolder.XmlSerializer = XmlSerializer;
- Utilities.CryptographyProvider = CryptographyProvider;
+ CollectionFolder.JsonSerializer = JsonSerializer;
+ CollectionFolder.ApplicationHost = this;
AuthenticatedAttribute.AuthService = AuthService;
}
@@ -1463,19 +1369,14 @@ namespace Emby.Server.Implementations
ConfigurationManager.SaveConfiguration();
}
- RegisterModules();
-
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- Plugins = GetExports<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
+ Plugins = GetExportsWithInfo<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
- HttpServer.Init(GetExports<IService>(false));
-
- ServerManager.AddWebSocketListeners(GetExports<IWebSocketListener>(false));
+ HttpServer.Init(GetExports<IService>(false), GetExports<IWebSocketListener>());
StartServer();
LibraryManager.AddParts(GetExports<IResolverIgnoreRule>(),
- GetExports<IVirtualFolderCreator>(),
GetExports<IItemResolver>(),
GetExports<IIntroProvider>(),
GetExports<IBaseItemComparer>(),
@@ -1493,18 +1394,21 @@ namespace Emby.Server.Implementations
SubtitleManager.AddParts(GetExports<ISubtitleProvider>());
- SessionManager.AddParts(GetExports<ISessionControllerFactory>());
-
ChannelManager.AddParts(GetExports<IChannel>());
MediaSourceManager.AddParts(GetExports<IMediaSourceProvider>());
NotificationManager.AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
- SyncManager.AddParts(GetExports<ISyncProvider>());
+ UserManager.AddParts(GetExports<IAuthenticationProvider>());
+
+ IsoManager.AddParts(GetExports<IIsoMounter>());
}
- private IPlugin LoadPlugin(IPlugin plugin)
+ private IPlugin LoadPlugin(Tuple<IPlugin, string> info)
{
+ var plugin = info.Item1;
+ var assemblyFilePath = info.Item2;
+
try
{
var assemblyPlugin = plugin as IPluginAssembly;
@@ -1514,10 +1418,9 @@ namespace Emby.Server.Implementations
var assembly = plugin.GetType().Assembly;
var assemblyName = assembly.GetName();
- var assemblyFileName = assemblyName.Name + ".dll";
- var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
+ var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
- assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version);
+ assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
try
{
@@ -1536,8 +1439,11 @@ namespace Emby.Server.Implementations
}
}
- var isFirstRun = !File.Exists(plugin.ConfigurationFilePath);
- plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s));
+ var hasPluginConfiguration = plugin as IHasPluginConfiguration;
+ if (hasPluginConfiguration != null)
+ {
+ hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
+ }
}
catch (Exception ex)
{
@@ -1553,18 +1459,32 @@ namespace Emby.Server.Implementations
/// </summary>
protected void DiscoverTypes()
{
- FailedAssemblies.Clear();
+ Logger.Info("Loading assemblies");
- var assemblies = GetComposablePartAssemblies().ToList();
+ var assemblyInfos = GetComposablePartAssemblies();
- foreach (var assembly in assemblies)
+ foreach (var assemblyInfo in assemblyInfos)
{
- Logger.Info("Loading {0}", assembly.FullName);
+ var assembly = assemblyInfo.Item1;
+ var path = assemblyInfo.Item2;
+
+ if (path == null)
+ {
+ Logger.Info("Loading {0}", assembly.FullName);
+ }
+ else
+ {
+ Logger.Info("Loading {0} from {1}", assembly.FullName, path);
+ }
}
- AllConcreteTypes = assemblies
+ AllConcreteTypes = assemblyInfos
.SelectMany(GetTypes)
- .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType)
+ .Where(info =>
+ {
+ var t = info.Item1;
+ return t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType;
+ })
.ToArray();
}
@@ -1572,22 +1492,21 @@ namespace Emby.Server.Implementations
/// Gets a list of types within an assembly
/// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference
/// </summary>
- /// <param name="assembly">The assembly.</param>
- /// <returns>IEnumerable{Type}.</returns>
- /// <exception cref="System.ArgumentNullException">assembly</exception>
- protected List<Type> GetTypes(Assembly assembly)
+ protected List<Tuple<Type, string>> GetTypes(Tuple<Assembly, string> assemblyInfo)
{
- if (assembly == null)
+ if (assemblyInfo == null)
{
- return new List<Type>();
+ return new List<Tuple<Type, string>>();
}
+ var assembly = assemblyInfo.Item1;
+
try
{
// This null checking really shouldn't be needed but adding it due to some
// unhandled exceptions in mono 5.0 that are a little hard to hunt down
var types = assembly.GetTypes() ?? new Type[] { };
- return types.Where(t => t != null).ToList();
+ return types.Where(t => t != null).Select(i => new Tuple<Type, string>(i, assemblyInfo.Item2)).ToList();
}
catch (ReflectionTypeLoadException ex)
{
@@ -1604,24 +1523,22 @@ namespace Emby.Server.Implementations
// If it fails we can still get a list of the Types it was able to resolve
var types = ex.Types ?? new Type[] { };
- return types.Where(t => t != null).ToList();
+ return types.Where(t => t != null).Select(i => new Tuple<Type, string>(i, assemblyInfo.Item2)).ToList();
}
catch (Exception ex)
{
Logger.ErrorException("Error loading types from assembly", ex);
- return new List<Type>();
+ return new List<Tuple<Type, string>>();
}
}
private CertificateInfo CertificateInfo { get; set; }
- private X509Certificate Certificate { get; set; }
+ protected X509Certificate Certificate { get; private set; }
private IEnumerable<string> GetUrlPrefixes()
{
- var hosts = new List<string>();
-
- hosts.Add("+");
+ var hosts = new[] { "+" };
return hosts.SelectMany(i =>
{
@@ -1639,6 +1556,8 @@ namespace Emby.Server.Implementations
});
}
+ protected abstract IHttpListener CreateHttpListener();
+
/// <summary>
/// Starts the server.
/// </summary>
@@ -1646,12 +1565,16 @@ namespace Emby.Server.Implementations
{
try
{
- ServerManager.Start(GetUrlPrefixes().ToArray());
+ ((HttpListenerHost)HttpServer).StartServer(GetUrlPrefixes().ToArray(), CreateHttpListener());
return;
}
catch (Exception ex)
{
- Logger.ErrorException("Error starting http server", ex);
+ var msg = string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase)
+ ? "The http server is unable to start due to a Socket error. This can occasionally happen when the operating system takes longer than usual to release the IP bindings from the previous session. This can take up to five minutes. Please try waiting or rebooting the system."
+ : "Error starting Http Server";
+
+ Logger.ErrorException(msg, ex);
if (HttpPort == ServerConfiguration.DefaultHttpPort)
{
@@ -1663,7 +1586,7 @@ namespace Emby.Server.Implementations
try
{
- ServerManager.Start(GetUrlPrefixes().ToArray());
+ ((HttpListenerHost)HttpServer).StartServer(GetUrlPrefixes().ToArray(), CreateHttpListener());
}
catch (Exception ex)
{
@@ -1822,10 +1745,9 @@ namespace Emby.Server.Implementations
/// Gets the composable part assemblies.
/// </summary>
/// <returns>IEnumerable{Assembly}.</returns>
- protected IEnumerable<Assembly> GetComposablePartAssemblies()
+ protected List<Tuple<Assembly, string>> GetComposablePartAssemblies()
{
- var list = GetPluginAssemblies()
- .ToList();
+ var list = GetPluginAssemblies();
// Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
// This will prevent the .dll file from getting locked, and allow us to replace it when needed
@@ -1863,10 +1785,13 @@ namespace Emby.Server.Implementations
// Local metadata
list.Add(GetAssembly(typeof(BoxSetXmlSaver)));
+ // Notifications
+ list.Add(GetAssembly(typeof(NotificationManager)));
+
// Xbmc
list.Add(GetAssembly(typeof(ArtistNfoProvider)));
- list.AddRange(GetAssembliesWithPartsInternal());
+ list.AddRange(GetAssembliesWithPartsInternal().Select(i => new Tuple<Assembly, string>(i, null)));
return list.ToList();
}
@@ -1877,25 +1802,92 @@ namespace Emby.Server.Implementations
/// Gets the plugin assemblies.
/// </summary>
/// <returns>IEnumerable{Assembly}.</returns>
- private IEnumerable<Assembly> GetPluginAssemblies()
+ private List<Tuple<Assembly, string>> GetPluginAssemblies()
+ {
+ // Copy pre-installed plugins
+ var sourcePath = Path.Combine(ApplicationPaths.ApplicationResourcesPath, "plugins");
+ CopyPlugins(sourcePath, ApplicationPaths.PluginsPath);
+
+ return GetPluginAssemblies(ApplicationPaths.PluginsPath);
+ }
+
+ private void CopyPlugins(string source, string target)
{
+ List<string> files;
+
try
{
- return Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
- .Where(EnablePlugin)
+ files = Directory.EnumerateFiles(source, "*.dll", SearchOption.TopDirectoryOnly)
+ .ToList();
+
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return;
+ }
+
+ if (files.Count == 0)
+ {
+ return;
+ }
+
+ foreach (var sourceFile in files)
+ {
+ var filename = Path.GetFileName(sourceFile);
+ var targetFile = Path.Combine(target, filename);
+
+ var targetFileExists = File.Exists(targetFile);
+
+ if (!targetFileExists && ServerConfigurationManager.Configuration.UninstalledPlugins.Contains(filename, StringComparer.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (targetFileExists && GetDllVersion(targetFile) >= GetDllVersion(sourceFile))
+ {
+ continue;
+ }
+
+ Directory.CreateDirectory(target);
+ File.Copy(sourceFile, targetFile, true);
+ }
+ }
+
+ private Version GetDllVersion(string path)
+ {
+ try
+ {
+ var result = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion);
+
+ Logger.Info("File {0} has version {1}", path, result);
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting version number from {0}", ex, path);
+
+ return new Version(1, 0);
+ }
+ }
+
+ private List<Tuple<Assembly, string>> GetPluginAssemblies(string path)
+ {
+ try
+ {
+ return FilterAssembliesToLoad(Directory.EnumerateFiles(path, "*.dll", SearchOption.TopDirectoryOnly))
.Select(LoadAssembly)
.Where(a => a != null)
.ToList();
}
catch (DirectoryNotFoundException)
{
- return new List<Assembly>();
+ return new List<Tuple<Assembly, string>>();
}
}
- private bool EnablePlugin(string path)
+ private IEnumerable<string> FilterAssembliesToLoad(IEnumerable<string> paths)
{
- var filename = Path.GetFileName(path);
var exclude = new[]
{
@@ -1903,6 +1895,7 @@ namespace Emby.Server.Implementations
"mbintros.dll",
"embytv.dll",
"Messenger.dll",
+ "Messages.dll",
"MediaBrowser.Plugins.TvMazeProvider.dll",
"MBBookshelf.dll",
"MediaBrowser.Channels.Adult.YouJizz.dll",
@@ -1927,10 +1920,49 @@ namespace Emby.Server.Implementations
"MediaBrowser.Channels.HitboxTV.dll",
"MediaBrowser.Channels.HockeyStreams.dll",
"MediaBrowser.Plugins.ITV.dll",
- "MediaBrowser.Plugins.Lastfm.dll"
+ "MediaBrowser.Plugins.Lastfm.dll",
+ "ServerRestart.dll",
+ "MediaBrowser.Plugins.NotifyMyAndroidNotifications.dll",
+ "MetadataViewer.dll"
+ };
+
+ var minRequiredVersions = new Dictionary<string, Version>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "GameBrowser.dll", new Version(3, 1) },
+ { "moviethemesongs.dll", new Version(1, 6) },
+ { "themesongs.dll", new Version(1, 2) }
};
- return !exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ return paths.Where(path =>
+ {
+ var filename = Path.GetFileName(path);
+ if (exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ Version minRequiredVersion;
+ if (minRequiredVersions.TryGetValue(filename, out minRequiredVersion))
+ {
+ try
+ {
+ var version = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion);
+
+ if (version < minRequiredVersion)
+ {
+ Logger.Info("Not loading {0} {1} because the minimum supported version is {2}. Please update to the newer version", filename, version, minRequiredVersion);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting version number from {0}", ex, path);
+
+ return false;
+ }
+ }
+ return true;
+ });
}
/// <summary>
@@ -1947,16 +1979,13 @@ namespace Emby.Server.Implementations
IsShuttingDown = IsShuttingDown,
Version = ApplicationVersion.ToString(),
WebSocketPortNumber = HttpPort,
- FailedPluginAssemblies = FailedAssemblies.ToArray(),
- InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray(),
CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
Id = SystemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath,
LogPath = ApplicationPaths.LogDirectoryPath,
- ItemsByNamePath = ApplicationPaths.ItemsByNamePath,
+ ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
CachePath = ApplicationPaths.CachePath,
- MacAddress = GetMacAddress(),
HttpServerPortNumber = HttpPort,
SupportsHttps = SupportsHttps,
HttpsPortNumber = HttpsPort,
@@ -1979,6 +2008,16 @@ namespace Emby.Server.Implementations
};
}
+ public WakeOnLanInfo[] GetWakeOnLanInfo()
+ {
+ return NetworkManager.GetMacAddresses()
+ .Select(i => new WakeOnLanInfo
+ {
+ MacAddress = i
+ })
+ .ToArray();
+ }
+
public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
{
var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
@@ -1998,7 +2037,7 @@ namespace Emby.Server.Implementations
{
get
{
- return SupportsHttps && (ServerConfigurationManager.Configuration.EnableHttps || ServerConfigurationManager.Configuration.RequireHttps);
+ return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
}
}
@@ -2127,14 +2166,20 @@ namespace Emby.Server.Implementations
return cachedResult;
}
+ var logPing = false;
+
+#if DEBUG
+ logPing = true;
+#endif
+
try
{
using (var response = await HttpClient.SendAsync(new HttpRequestOptions
{
Url = apiUrl,
LogErrorResponseBody = false,
- LogErrors = false,
- LogRequest = false,
+ LogErrors = logPing,
+ LogRequest = logPing,
TimeoutMs = 30000,
BufferContent = false,
@@ -2158,9 +2203,9 @@ namespace Emby.Server.Implementations
Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled");
throw;
}
- catch
+ catch (Exception ex)
{
- Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
+ Logger.Debug("Ping test result to {0}. Success: {1} {2}", apiUrl, false, ex.Message);
_validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
return false;
@@ -2171,7 +2216,7 @@ namespace Emby.Server.Implementations
{
get
{
- return string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName)
+ return string.IsNullOrEmpty(ServerConfigurationManager.Configuration.ServerName)
? Environment.MachineName
: ServerConfigurationManager.Configuration.ServerName;
}
@@ -2182,23 +2227,6 @@ namespace Emby.Server.Implementations
public int HttpsPort { get; private set; }
/// <summary>
- /// Gets the mac address.
- /// </summary>
- /// <returns>System.String.</returns>
- private string GetMacAddress()
- {
- try
- {
- return NetworkManager.GetMacAddress();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting mac address", ex);
- return null;
- }
- }
-
- /// <summary>
/// Shuts down.
/// </summary>
public async Task Shutdown()
@@ -2296,7 +2324,7 @@ namespace Emby.Server.Implementations
try
{
var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
- "Emby",
+ "Emby.Releases",
ApplicationVersion,
updateLevel,
ReleaseAssetFilename,
@@ -2370,7 +2398,7 @@ namespace Emby.Server.Implementations
/// <returns>The hostname in <paramref name="externalDns"/></returns>
private static string GetHostnameFromExternalDns(string externalDns)
{
- if (string.IsNullOrWhiteSpace(externalDns))
+ if (string.IsNullOrEmpty(externalDns))
{
return "localhost";
}
@@ -2424,25 +2452,6 @@ namespace Emby.Server.Implementations
{
}
- private void RegisterModules()
- {
- var moduleTypes = GetExportTypes<IDependencyModule>();
-
- foreach (var type in moduleTypes)
- {
- try
- {
- var instance = Activator.CreateInstance(type) as IDependencyModule;
- if (instance != null)
- instance.BindDependencies(this);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error setting up dependency bindings for " + type.Name, ex);
- }
- }
- }
-
/// <summary>
/// Called when [application updated].
/// </summary>
@@ -2471,7 +2480,6 @@ namespace Emby.Server.Implementations
_disposed = true;
Dispose(true);
- GC.SuppressFinalize(this);
}
}
@@ -2507,19 +2515,50 @@ namespace Emby.Server.Implementations
}
}
- void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime)
+ private Dictionary<string, string> _values;
+ public string GetValue(string name)
{
- RegisterSingleInstance(obj, manageLifetime);
- }
+ if (_values == null)
+ {
+ _values = LoadValues();
+ }
- void IDependencyContainer.RegisterSingleInstance<T>(Func<T> func)
- {
- RegisterSingleInstance(func);
+ string value;
+
+ if (_values.TryGetValue(name, out value))
+ {
+ return value;
+ }
+
+ return null;
}
- void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
+ private Dictionary<string, string> LoadValues()
{
- Container.Register(typeInterface, typeImplementation);
+ Dictionary<string, string> values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ using (var stream = typeof(ApplicationHost).Assembly.GetManifestResourceStream(typeof(ApplicationHost).Namespace + ".values.txt"))
+ {
+ using (var reader = new StreamReader(stream))
+ {
+ while (!reader.EndOfStream)
+ {
+ var line = reader.ReadLine();
+ if (string.IsNullOrEmpty(line))
+ {
+ continue;
+ }
+
+ var index = line.IndexOf('=');
+ if (index != -1)
+ {
+ values[line.Substring(0, index)] = line.Substring(index + 1);
+ }
+ }
+ }
+ }
+
+ return values;
}
}
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
index 32938e151..fd61f2617 100644
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Server.Implementations/Archiving/ZipClient.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.IO;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
+using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Zip;
@@ -185,44 +186,5 @@ namespace Emby.Server.Implementations.Archiving
}
}
}
-
- /// <summary>
- /// Extracts all from rar.
- /// </summary>
- /// <param name="sourceFile">The source file.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles)
- {
- using (var fileStream = _fileSystem.OpenRead(sourceFile))
- {
- ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles);
- }
- }
-
- /// <summary>
- /// Extracts all from rar.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- public void ExtractAllFromRar(Stream source, string targetPath, bool overwriteExistingFiles)
- {
- using (var archive = RarArchive.Open(source))
- {
- using (var reader = archive.ExtractAllEntries())
- {
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
-
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
-
- reader.WriteAllToDirectory(targetPath, options);
- }
- }
- }
}
}
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index 71497f6bf..007f60a9b 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.Browser
/// </summary>
/// <param name="page">The page.</param>
/// <param name="appHost">The app host.</param>
- public static void OpenDashboardPage(string page, IServerApplicationHost appHost)
+ private static void OpenDashboardPage(string page, IServerApplicationHost appHost)
{
var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page;
@@ -21,37 +21,15 @@ namespace Emby.Server.Implementations.Browser
}
/// <summary>
- /// Opens the community.
- /// </summary>
- public static void OpenCommunity(IServerApplicationHost appHost)
- {
- OpenUrl(appHost, "http://emby.media/community");
- }
-
- public static void OpenEmbyPremiere(IServerApplicationHost appHost)
- {
- OpenDashboardPage("supporterkey.html", appHost);
- }
-
- /// <summary>
/// Opens the web client.
/// </summary>
/// <param name="appHost">The app host.</param>
- public static void OpenWebClient(IServerApplicationHost appHost)
+ public static void OpenWebApp(IServerApplicationHost appHost)
{
OpenDashboardPage("index.html", appHost);
}
/// <summary>
- /// Opens the dashboard.
- /// </summary>
- /// <param name="appHost">The app host.</param>
- public static void OpenDashboard(IServerApplicationHost appHost)
- {
- OpenDashboardPage("dashboard.html", appHost);
- }
-
- /// <summary>
/// Opens the URL.
/// </summary>
/// <param name="url">The URL.</param>
diff --git a/Emby.Server.Implementations/Channels/ChannelConfigurations.cs b/Emby.Server.Implementations/Channels/ChannelConfigurations.cs
deleted file mode 100644
index ef0973e7f..000000000
--- a/Emby.Server.Implementations/Channels/ChannelConfigurations.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-using System.Collections.Generic;
-
-namespace Emby.Server.Implementations.Channels
-{
- public static class ChannelConfigurationExtension
- {
- public static ChannelOptions GetChannelsConfiguration(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<ChannelOptions>("channels");
- }
- }
-
- public class ChannelConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "channels",
- ConfigurationType = typeof (ChannelOptions)
- }
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index 7be4101c8..8448d3640 100644
--- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -18,24 +18,17 @@ namespace Emby.Server.Implementations.Channels
_channelManager = (ChannelManager)channelManager;
}
- public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
+ public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
{
- var baseItem = (BaseItem) item;
-
- if (baseItem.SourceType == SourceType.Channel)
+ if (item.SourceType == SourceType.Channel)
{
- return _channelManager.GetDynamicMediaSources(baseItem, cancellationToken);
+ return _channelManager.GetDynamicMediaSources(item, cancellationToken);
}
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
- public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task CloseMediaSource(string liveStreamId)
+ public Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
index 0c363c585..a6643e83c 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(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return GetChannel(item).GetSupportedChannelImages();
}
- public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
var channel = GetChannel(item);
@@ -35,19 +35,19 @@ namespace Emby.Server.Implementations.Channels
get { return "Channel Image Provider"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Channel;
}
- private IChannel GetChannel(IHasMetadata item)
+ private IChannel GetChannel(BaseItem item)
{
var channel = (Channel)item;
return ((ChannelManager)_channelManager).GetChannelProvider(channel);
}
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
return GetSupportedImages(item).Any(i => !item.HasImage(i));
}
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index c566ca25b..e832c7c6f 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Channels
private readonly IProviderManager _providerManager;
private readonly ILocalizationManager _localization;
- private readonly ConcurrentDictionary<Guid, bool> _refreshedItems = new ConcurrentDictionary<Guid, bool>();
public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, ILocalizationManager localization, IHttpClient httpClient, IProviderManager providerManager)
{
@@ -72,7 +71,7 @@ namespace Emby.Server.Implementations.Channels
{
get
{
- return TimeSpan.FromHours(6);
+ return TimeSpan.FromHours(3);
}
}
@@ -81,6 +80,51 @@ namespace Emby.Server.Implementations.Channels
Channels = channels.ToArray();
}
+ public bool EnableMediaSourceDisplay(BaseItem item)
+ {
+ var internalChannel = _libraryManager.GetItemById(item.ChannelId);
+ var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
+
+ return !(channel is IDisableMediaSourceDisplay);
+ }
+
+ public bool CanDelete(BaseItem item)
+ {
+ var internalChannel = _libraryManager.GetItemById(item.ChannelId);
+ var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
+
+ var supportsDelete = channel as ISupportsDelete;
+ return supportsDelete != null && supportsDelete.CanDelete(item);
+ }
+
+ public bool EnableMediaProbe(BaseItem item)
+ {
+ var internalChannel = _libraryManager.GetItemById(item.ChannelId);
+ var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
+
+ return channel is ISupportsMediaProbe;
+ }
+
+ public Task DeleteItem(BaseItem item)
+ {
+ var internalChannel = _libraryManager.GetItemById(item.ChannelId);
+ if (internalChannel == null)
+ {
+ throw new ArgumentException();
+ }
+
+ var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
+
+ var supportsDelete = channel as ISupportsDelete;
+
+ if (supportsDelete == null)
+ {
+ throw new ArgumentException();
+ }
+
+ return supportsDelete.DeleteItem(item.ExternalId, CancellationToken.None);
+ }
+
private IEnumerable<IChannel> GetAllChannels()
{
return Channels
@@ -92,9 +136,9 @@ namespace Emby.Server.Implementations.Channels
return GetAllChannels().Select(i => GetInternalChannelId(i.Name));
}
- public Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken)
+ public QueryResult<Channel> GetChannelsInternal(ChannelQuery query)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
+ var user = query.UserId.Equals(Guid.Empty)
? null
: _userManager.GetUserById(query.UserId);
@@ -103,6 +147,25 @@ namespace Emby.Server.Implementations.Channels
.OrderBy(i => i.SortName)
.ToList();
+ if (query.IsRecordingsFolder.HasValue)
+ {
+ var val = query.IsRecordingsFolder.Value;
+ channels = channels.Where(i =>
+ {
+ try
+ {
+ var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes;
+
+ return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
+ }
+ catch
+ {
+ return false;
+ }
+
+ }).ToList();
+ }
+
if (query.SupportsLatestItems.HasValue)
{
var val = query.SupportsLatestItems.Value;
@@ -119,6 +182,23 @@ namespace Emby.Server.Implementations.Channels
}).ToList();
}
+
+ if (query.SupportsMediaDeletion.HasValue)
+ {
+ var val = query.SupportsMediaDeletion.Value;
+ channels = channels.Where(i =>
+ {
+ try
+ {
+ return GetChannelProvider(i) is ISupportsDelete == val;
+ }
+ catch
+ {
+ return false;
+ }
+
+ }).ToList();
+ }
if (query.IsFavorite.HasValue)
{
var val = query.IsFavorite.Value;
@@ -161,22 +241,29 @@ namespace Emby.Server.Implementations.Channels
var returnItems = all.ToArray(all.Count);
- var result = new QueryResult<Channel>
+ if (query.RefreshLatestChannelItems)
+ {
+ foreach (var item in returnItems)
+ {
+ var task = RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None);
+ Task.WaitAll(task);
+ }
+ }
+
+ return new QueryResult<Channel>
{
Items = returnItems,
TotalRecordCount = totalCount
};
-
- return Task.FromResult(result);
}
- public async Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
+ public QueryResult<BaseItemDto> GetChannels(ChannelQuery query)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
+ var user = query.UserId.Equals(Guid.Empty)
? null
: _userManager.GetUserById(query.UserId);
- var internalResult = await GetChannelsInternal(query, cancellationToken).ConfigureAwait(false);
+ var internalResult = GetChannelsInternal(query);
var dtoOptions = new DtoOptions()
{
@@ -195,8 +282,6 @@ namespace Emby.Server.Implementations.Channels
public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
{
- _refreshedItems.Clear();
-
var allChannelsList = GetAllChannels().ToList();
var numComplete = 0;
@@ -230,7 +315,7 @@ namespace Emby.Server.Implementations.Channels
private Channel GetChannelEntity(IChannel channel)
{
- var item = GetChannel(GetInternalChannelId(channel.Name).ToString("N"));
+ var item = GetChannel(GetInternalChannelId(channel.Name));
if (item == null)
{
@@ -240,23 +325,23 @@ namespace Emby.Server.Implementations.Channels
return item;
}
- private List<ChannelMediaInfo> GetSavedMediaSources(BaseItem item)
+ private List<MediaSourceInfo> GetSavedMediaSources(BaseItem item)
{
- var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
+ var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
try
{
- return _jsonSerializer.DeserializeFromFile<List<ChannelMediaInfo>>(path) ?? new List<ChannelMediaInfo>();
+ return _jsonSerializer.DeserializeFromFile<List<MediaSourceInfo>>(path) ?? new List<MediaSourceInfo>();
}
catch
{
- return new List<ChannelMediaInfo>();
+ return new List<MediaSourceInfo>();
}
}
- private void SaveMediaSources(BaseItem item, List<ChannelMediaInfo> mediaSources)
+ private void SaveMediaSources(BaseItem item, List<MediaSourceInfo> mediaSources)
{
- var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
+ var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
if (mediaSources == null || mediaSources.Count == 0)
{
@@ -278,10 +363,10 @@ namespace Emby.Server.Implementations.Channels
public IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken)
{
- IEnumerable<ChannelMediaInfo> results = GetSavedMediaSources(item);
+ IEnumerable<MediaSourceInfo> results = GetSavedMediaSources(item);
- return SortMediaInfoResults(results)
- .Select(i => GetMediaSource(item, i))
+ return results
+ .Select(i => NormalizeMediaSource(item, i))
.ToList();
}
@@ -292,7 +377,7 @@ namespace Emby.Server.Implementations.Channels
var requiresCallback = channelPlugin as IRequiresMediaInfoCallback;
- IEnumerable<ChannelMediaInfo> results;
+ IEnumerable<MediaSourceInfo> results;
if (requiresCallback != null)
{
@@ -301,20 +386,20 @@ namespace Emby.Server.Implementations.Channels
}
else
{
- results = new List<ChannelMediaInfo>();
+ results = new List<MediaSourceInfo>();
}
- return SortMediaInfoResults(results)
- .Select(i => GetMediaSource(item, i))
+ return results
+ .Select(i => NormalizeMediaSource(item, i))
.ToList();
}
- private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo =
- new ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>>();
+ private readonly ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>> _channelItemMediaInfo =
+ new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
- private async Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken)
+ private async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken)
{
- Tuple<DateTime, List<ChannelMediaInfo>> cachedInfo;
+ Tuple<DateTime, List<MediaSourceInfo>> cachedInfo;
if (_channelItemMediaInfo.TryGetValue(id, out cachedInfo))
{
@@ -328,56 +413,24 @@ namespace Emby.Server.Implementations.Channels
.ConfigureAwait(false);
var list = mediaInfo.ToList();
- var item2 = new Tuple<DateTime, List<ChannelMediaInfo>>(DateTime.UtcNow, list);
+ var item2 = new Tuple<DateTime, List<MediaSourceInfo>>(DateTime.UtcNow, list);
_channelItemMediaInfo.AddOrUpdate(id, item2, (key, oldValue) => item2);
return list;
}
- private MediaSourceInfo GetMediaSource(BaseItem item, ChannelMediaInfo info)
+ private MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info)
{
- var source = info.ToMediaSource(item.Id);
+ info.RunTimeTicks = info.RunTimeTicks ?? item.RunTimeTicks;
- source.RunTimeTicks = source.RunTimeTicks ?? item.RunTimeTicks;
-
- return source;
- }
-
- private IEnumerable<ChannelMediaInfo> SortMediaInfoResults(IEnumerable<ChannelMediaInfo> channelMediaSources)
- {
- var list = channelMediaSources.ToList();
-
- var options = _config.GetChannelsConfiguration();
-
- var width = options.PreferredStreamingWidth;
-
- if (width.HasValue)
- {
- var val = width.Value;
-
- var res = list
- .OrderBy(i => i.Width.HasValue && i.Width.Value <= val ? 0 : 1)
- .ThenBy(i => Math.Abs((i.Width ?? 0) - val))
- .ThenByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf)
- .ToList();
-
-
- return res;
- }
-
- return list
- .OrderByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf);
+ return info;
}
private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken)
{
- var parentFolder = GetInternalChannelFolder(cancellationToken);
- var parentFolderId = parentFolder.Id;
+ var parentFolderId = Guid.Empty;
var id = GetInternalChannelId(channelInfo.Name);
- var idString = id.ToString("N");
var path = Channel.GetInternalMetadataPath(_config.ApplicationPaths.InternalMetadataPath, id);
@@ -405,11 +458,11 @@ namespace Emby.Server.Implementations.Channels
}
item.Path = path;
- if (!string.Equals(item.ChannelId, idString, StringComparison.OrdinalIgnoreCase))
+ if (!item.ChannelId.Equals(id))
{
forceUpdate = true;
}
- item.ChannelId = idString;
+ item.ChannelId = id;
if (item.ParentId != parentFolderId)
{
@@ -419,25 +472,24 @@ namespace Emby.Server.Implementations.Channels
item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating);
item.Overview = channelInfo.Description;
- item.HomePageUrl = channelInfo.HomePageUrl;
if (string.IsNullOrWhiteSpace(item.Name))
{
item.Name = channelInfo.Name;
}
- item.OnMetadataChanged();
-
if (isNew)
{
- _libraryManager.CreateItem(item, cancellationToken);
+ item.OnMetadataChanged();
+ _libraryManager.CreateItem(item, null);
}
- else if (forceUpdate)
+
+ await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
{
- item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
- }
+ ForceSave = !isNew && forceUpdate
+
+ }, cancellationToken);
- await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken);
return item;
}
@@ -458,6 +510,11 @@ namespace Emby.Server.Implementations.Channels
}
}
+ public Channel GetChannel(Guid id)
+ {
+ return _libraryManager.GetItemById(id) as Channel;
+ }
+
public Channel GetChannel(string id)
{
return _libraryManager.GetItemById(id) as Channel;
@@ -468,14 +525,14 @@ namespace Emby.Server.Implementations.Channels
return _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Channel).Name },
- OrderBy = new Tuple<string, SortOrder>[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
+ OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
}).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray();
}
public ChannelFeatures GetChannelFeatures(string id)
{
- if (string.IsNullOrWhiteSpace(id))
+ if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
@@ -486,13 +543,8 @@ namespace Emby.Server.Implementations.Channels
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
}
- public bool SupportsSync(string channelId)
+ public bool SupportsExternalTransfer(Guid channelId)
{
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
//var channel = GetChannel(channelId);
var channelProvider = GetChannelProvider(channelId);
@@ -503,7 +555,6 @@ namespace Emby.Server.Implementations.Channels
IChannel provider,
InternalChannelFeatures features)
{
- var isIndexable = provider is IIndexableChannel;
var supportsLatest = provider is ISupportsLatestMedia;
return new ChannelFeatures
@@ -518,57 +569,28 @@ namespace Emby.Server.Implementations.Channels
SupportsLatestMedia = supportsLatest,
Name = channel.Name,
Id = channel.Id.ToString("N"),
- SupportsContentDownloading = features.SupportsContentDownloading && (isIndexable || supportsLatest),
+ SupportsContentDownloading = features.SupportsContentDownloading,
AutoRefreshLevels = features.AutoRefreshLevels
};
}
private Guid GetInternalChannelId(string name)
{
- if (string.IsNullOrWhiteSpace(name))
+ if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
}
- public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(AllChannelMediaQuery query, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var limit = query.Limit;
-
- // See below about parental control
- if (user != null)
- {
- query.StartIndex = null;
- query.Limit = null;
- }
-
var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false);
var items = internalResult.Items;
var totalRecordCount = internalResult.TotalRecordCount;
- // Supporting parental control is a hack because it has to be done after querying the remote data source
- // This will get screwy if apps try to page, so limit to 10 results in an attempt to always keep them on the first page
- if (user != null)
- {
- items = items.Where(i => i.IsVisible(user))
- .Take(limit ?? 10)
- .ToArray();
-
- totalRecordCount = items.Length;
- }
-
- var dtoOptions = new DtoOptions()
- {
- Fields = query.Fields
- };
-
- var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user);
+ var returnItems = _dtoService.GetBaseItemDtos(items, query.DtoOptions, query.User);
var result = new QueryResult<BaseItemDto>
{
@@ -579,407 +601,150 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(AllChannelMediaQuery query, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- if (!string.IsNullOrWhiteSpace(query.UserId) && user == null)
- {
- throw new ArgumentException("User not found.");
- }
-
- var channels = GetAllChannels();
+ var channels = GetAllChannels().Where(i => i is ISupportsLatestMedia).ToArray();
if (query.ChannelIds.Length > 0)
{
// Avoid implicitly captured closure
var ids = query.ChannelIds;
channels = channels
- .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N")))
+ .Where(i => ids.Contains(GetInternalChannelId(i.Name)))
.ToArray();
}
- // Avoid implicitly captured closure
- var userId = query.UserId;
-
- var tasks = channels
- .Select(async i =>
- {
- var indexable = i as ISupportsLatestMedia;
-
- if (indexable != null)
- {
- try
- {
- var result = await GetLatestItems(indexable, i, userId, cancellationToken).ConfigureAwait(false);
-
- var resultItems = result.ToList();
-
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult
- {
- Items = resultItems,
- TotalRecordCount = resultItems.Count
- });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting all media from {0}", ex, i.Name);
- }
- }
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult());
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- var totalCount = results.Length;
-
- IEnumerable<Tuple<IChannel, ChannelItemInfo>> items = results
- .SelectMany(i => i.Item2.Items.Select(m => new Tuple<IChannel, ChannelItemInfo>(i.Item1, m)));
-
- if (query.ContentTypes.Length > 0)
- {
- // Avoid implicitly captured closure
- var contentTypes = query.ContentTypes;
-
- items = items.Where(i => contentTypes.Contains(i.Item2.ContentType));
- }
- if (query.ExtraTypes.Length > 0)
+ if (channels.Length == 0)
{
- // Avoid implicitly captured closure
- var contentTypes = query.ExtraTypes;
-
- items = items.Where(i => contentTypes.Contains(i.Item2.ExtraType));
+ return new QueryResult<BaseItem>();
}
- // Avoid implicitly captured closure
- var token = cancellationToken;
- var internalItems = items.Select(i =>
- {
- var channelProvider = i.Item1;
- var internalChannelId = GetInternalChannelId(channelProvider.Name);
- return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token);
- }).ToArray();
-
- internalItems = ApplyFilters(internalItems, query.Filters, user).ToArray();
- RefreshIfNeeded(internalItems);
-
- if (query.StartIndex.HasValue)
+ foreach (var channel in channels)
{
- internalItems = internalItems.Skip(query.StartIndex.Value).ToArray();
- }
- if (query.Limit.HasValue)
- {
- internalItems = internalItems.Take(query.Limit.Value).ToArray();
+ await RefreshLatestChannelItems(channel, cancellationToken).ConfigureAwait(false);
}
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = internalItems
- };
- }
+ query.IsFolder = false;
- private async Task<IEnumerable<ChannelItemInfo>> GetLatestItems(ISupportsLatestMedia indexable, IChannel channel, string userId, CancellationToken cancellationToken)
- {
- var cacheLength = CacheLength;
- var cachePath = GetChannelDataCachePath(channel, userId, "channelmanager-latest", null, false);
+ // hack for trailers, figure out a better way later
+ var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.IndexOf("Trailer") != -1;
- try
+ if (sortByPremiereDate)
{
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
+ query.OrderBy = new []
{
- return _jsonSerializer.DeserializeFromFile<List<ChannelItemInfo>>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
-
- }
-
- await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<List<ChannelItemInfo>>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
-
- }
-
- var result = await indexable.GetLatestMedia(new ChannelLatestMediaSearch
- {
- UserId = userId
-
- }, cancellationToken).ConfigureAwait(false);
-
- var resultItems = result.ToList();
-
- CacheResponse(resultItems, cachePath);
-
- return resultItems;
- }
- finally
- {
- _resourcePool.Release();
+ new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
+ new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending),
+ new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending)
+ };
}
- }
-
- public async Task<QueryResult<BaseItem>> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var channels = GetAllChannels();
-
- if (query.ChannelIds.Length > 0)
+ else
{
- // Avoid implicitly captured closure
- var ids = query.ChannelIds;
- channels = channels
- .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N")))
- .ToArray();
- }
-
- var tasks = channels
- .Select(async i =>
+ query.OrderBy = new []
{
- var indexable = i as IIndexableChannel;
-
- if (indexable != null)
- {
- try
- {
- var result = await GetAllItems(indexable, i, new InternalAllChannelMediaQuery
- {
- UserId = query.UserId,
- ContentTypes = query.ContentTypes,
- ExtraTypes = query.ExtraTypes,
- TrailerTypes = query.TrailerTypes
-
- }, cancellationToken).ConfigureAwait(false);
-
- return new Tuple<IChannel, ChannelItemResult>(i, result);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting all media from {0}", ex, i.Name);
- }
- }
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult());
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- var totalCount = results.Length;
-
- IEnumerable<Tuple<IChannel, ChannelItemInfo>> items = results
- .SelectMany(i => i.Item2.Items.Select(m => new Tuple<IChannel, ChannelItemInfo>(i.Item1, m)))
- .OrderBy(i => i.Item2.Name);
-
- if (query.StartIndex.HasValue)
- {
- items = items.Skip(query.StartIndex.Value);
- }
- if (query.Limit.HasValue)
- {
- items = items.Take(query.Limit.Value);
+ new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending)
+ };
}
- // Avoid implicitly captured closure
- var token = cancellationToken;
- var internalItems = items.Select(i =>
- {
- var channelProvider = i.Item1;
- var internalChannelId = GetInternalChannelId(channelProvider.Name);
- return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token);
- }).ToArray();
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = internalItems
- };
+ return _libraryManager.GetItemsResult(query);
}
- public async Task<QueryResult<BaseItemDto>> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken)
+ private async Task RefreshLatestChannelItems(IChannel channel, CancellationToken cancellationToken)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var internalResult = await GetAllMediaInternal(query, cancellationToken).ConfigureAwait(false);
-
- RefreshIfNeeded(internalResult.Items);
-
- var dtoOptions = new DtoOptions()
- {
- Fields = query.Fields
- };
+ var internalChannel = await GetChannel(channel, cancellationToken);
- var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user);
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
+ var query = new InternalItemsQuery();
+ query.Parent = internalChannel;
+ query.EnableTotalRecordCount = false;
+ query.ChannelIds = new Guid[] { internalChannel.Id };
- return result;
- }
-
- private async Task<ChannelItemResult> GetAllItems(IIndexableChannel indexable, IChannel channel, InternalAllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var cacheLength = CacheLength;
- var folderId = _jsonSerializer.SerializeToString(query).GetMD5().ToString("N");
- var cachePath = GetChannelDataCachePath(channel, query.UserId, folderId, null, false);
-
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
+ var result = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
- }
- catch (IOException)
+ foreach (var item in result.Items)
{
+ var folder = item as Folder;
- }
-
- await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- try
+ if (folder != null)
{
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
+ await GetChannelItemsInternal(new InternalItemsQuery
{
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
+ Parent = folder,
+ EnableTotalRecordCount = false,
+ ChannelIds = new Guid[] { internalChannel.Id }
+ }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
}
-
- var result = await indexable.GetAllMedia(query, cancellationToken).ConfigureAwait(false);
-
- CacheResponse(result, cachePath);
-
- return result;
- }
- finally
- {
- _resourcePool.Release();
}
}
- public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, IProgress<double> progress, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken)
{
// Get the internal channel entity
- var channel = GetChannel(query.ChannelId);
+ var channel = GetChannel(query.ChannelIds[0]);
// Find the corresponding channel provider plugin
var channelProvider = GetChannelProvider(channel);
- var channelInfo = channelProvider.GetChannelFeatures();
-
- int? providerStartIndex = null;
- int? providerLimit = null;
-
- if (channelInfo.MaxPageSize.HasValue)
- {
- providerStartIndex = query.StartIndex;
-
- if (query.Limit.HasValue && query.Limit.Value > channelInfo.MaxPageSize.Value)
- {
- query.Limit = Math.Min(query.Limit.Value, channelInfo.MaxPageSize.Value);
- }
- providerLimit = query.Limit;
-
- // This will cause some providers to fail
- if (providerLimit == 0)
- {
- providerLimit = 1;
- }
- }
-
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
+ var user = query.User;
ChannelItemSortField? sortField = null;
- ChannelItemSortField parsedField;
var sortDescending = false;
- if (query.OrderBy.Length == 1 &&
- Enum.TryParse(query.OrderBy[0].Item1, true, out parsedField))
- {
- sortField = parsedField;
- sortDescending = query.OrderBy[0].Item2 == SortOrder.Descending;
- }
+ var parentItem = !query.ParentId.Equals(Guid.Empty) ? _libraryManager.GetItemById(query.ParentId) : channel;
var itemsResult = await GetChannelItems(channelProvider,
user,
- query.FolderId,
- providerStartIndex,
- providerLimit,
+ parentItem is Channel ? null : parentItem.ExternalId,
sortField,
sortDescending,
cancellationToken)
.ConfigureAwait(false);
- var providerTotalRecordCount = providerLimit.HasValue ? itemsResult.TotalRecordCount : null;
+ if (query.ParentId.Equals(Guid.Empty))
+ {
+ query.Parent = channel;
+ }
+ query.ChannelIds = Array.Empty<Guid>();
- var internalItems = itemsResult.Items.Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, cancellationToken)).ToArray();
+ // Not yet sure why this is causing a problem
+ query.GroupByPresentationUniqueKey = false;
- if (user != null)
+ //_logger.Debug("GetChannelItemsInternal");
+
+ // null if came from cache
+ if (itemsResult != null)
{
- internalItems = internalItems.Where(i => i.IsVisible(user)).ToArray();
+ var internalItems = itemsResult.Items
+ .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken))
+ .ToArray();
+
+ var existingIds = _libraryManager.GetItemIds(query);
+ var deadIds = existingIds.Except(internalItems.Select(i => i.Id))
+ .ToArray();
- if (providerTotalRecordCount.HasValue)
+ foreach (var deadId in deadIds)
{
- providerTotalRecordCount = providerTotalRecordCount.Value;
+ var deadItem = _libraryManager.GetItemById(deadId);
+ if (deadItem != null)
+ {
+ _libraryManager.DeleteItem(deadItem, new DeleteOptions
+ {
+ DeleteFileLocation = false,
+ DeleteFromExternalProvider = false
+
+ }, parentItem, false);
+ }
}
}
- return GetReturnItems(internalItems, providerTotalRecordCount, user, query);
+ return _libraryManager.GetItemsResult(query);
}
- public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
{
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
var internalResult = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
- var dtoOptions = new DtoOptions()
- {
- Fields = query.Fields
- };
-
- var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user);
+ var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, query.DtoOptions, query.User);
var result = new QueryResult<BaseItemDto>
{
@@ -993,29 +758,24 @@ namespace Emby.Server.Implementations.Channels
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
User user,
- string folderId,
- int? startIndex,
- int? limit,
+ string externalFolderId,
ChannelItemSortField? sortField,
bool sortDescending,
CancellationToken cancellationToken)
{
- var userId = user.Id.ToString("N");
+ var userId = user == null ? null : user.Id.ToString("N");
var cacheLength = CacheLength;
- var cachePath = GetChannelDataCachePath(channel, userId, folderId, sortField, sortDescending);
+ var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
try
{
- if (!startIndex.HasValue && !limit.HasValue)
+ if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
{
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
+ var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ if (cachedResult != null)
{
- var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- if (cachedResult != null)
- {
- return cachedResult;
- }
+ return null;
}
}
}
@@ -1034,15 +794,12 @@ namespace Emby.Server.Implementations.Channels
{
try
{
- if (!startIndex.HasValue && !limit.HasValue)
+ if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
{
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
+ var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ if (cachedResult != null)
{
- var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- if (cachedResult != null)
- {
- return cachedResult;
- }
+ return null;
}
}
}
@@ -1057,19 +814,13 @@ namespace Emby.Server.Implementations.Channels
var query = new InternalChannelItemQuery
{
- UserId = userId,
- StartIndex = startIndex,
- Limit = limit,
+ UserId = user == null ? Guid.Empty : user.Id,
SortBy = sortField,
- SortDescending = sortDescending
+ SortDescending = sortDescending,
+ FolderId = externalFolderId
};
- if (!string.IsNullOrWhiteSpace(folderId))
- {
- var categoryItem = _libraryManager.GetItemById(new Guid(folderId));
-
- query.FolderId = categoryItem.ExternalId;
- }
+ query.FolderId = externalFolderId;
var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
@@ -1078,10 +829,7 @@ namespace Emby.Server.Implementations.Channels
throw new InvalidOperationException("Channel returned a null result from GetChannelItems");
}
- if (!startIndex.HasValue && !limit.HasValue)
- {
- CacheResponse(result, cachePath);
- }
+ CacheResponse(result, cachePath);
return result;
}
@@ -1107,7 +855,7 @@ namespace Emby.Server.Implementations.Channels
private string GetChannelDataCachePath(IChannel channel,
string userId,
- string folderId,
+ string externalFolderId,
ChannelItemSortField? sortField,
bool sortDescending)
{
@@ -1121,10 +869,10 @@ namespace Emby.Server.Implementations.Channels
userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
}
- var filename = string.IsNullOrWhiteSpace(folderId) ? "root" : folderId;
+ var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N");
filename += userCacheKey;
- var version = (channel.DataVersion ?? string.Empty).GetMD5().ToString("N");
+ var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N");
if (sortField.HasValue)
{
@@ -1144,40 +892,6 @@ namespace Emby.Server.Implementations.Channels
filename + ".json");
}
- private QueryResult<BaseItem> GetReturnItems(IEnumerable<BaseItem> items,
- int? totalCountFromProvider,
- User user,
- ChannelItemQuery query)
- {
- items = ApplyFilters(items, query.Filters, user);
-
- items = _libraryManager.Sort(items, user, query.OrderBy);
-
- var all = items.ToList();
- var totalCount = totalCountFromProvider ?? all.Count;
-
- if (!totalCountFromProvider.HasValue)
- {
- if (query.StartIndex.HasValue)
- {
- all = all.Skip(query.StartIndex.Value).ToList();
- }
- if (query.Limit.HasValue)
- {
- all = all.Take(query.Limit.Value).ToList();
- }
- }
-
- var returnItemArray = all.ToArray(all.Count);
- RefreshIfNeeded(returnItemArray);
-
- return new QueryResult<BaseItem>
- {
- Items = returnItemArray,
- TotalRecordCount = totalCount
- };
- }
-
private string GetIdToHash(string externalId, string channelName)
{
// Increment this as needed to force new downloads
@@ -1185,7 +899,7 @@ namespace Emby.Server.Implementations.Channels
return externalId + (channelName ?? string.Empty) + "16";
}
- private T GetItemById<T>(string idString, string channelName, string channnelDataVersion, out bool isNew)
+ private T GetItemById<T>(string idString, string channelName, out bool isNew)
where T : BaseItem, new()
{
var id = GetIdToHash(idString, channelName).GetMBId(typeof(T));
@@ -1201,7 +915,7 @@ namespace Emby.Server.Implementations.Channels
_logger.ErrorException("Error retrieving channel item from database", ex);
}
- if (item == null || !string.Equals(item.ExternalEtag, channnelDataVersion, StringComparison.Ordinal))
+ if (item == null)
{
item = new T();
isNew = true;
@@ -1211,13 +925,14 @@ namespace Emby.Server.Implementations.Channels
isNew = false;
}
- item.ExternalEtag = channnelDataVersion;
item.Id = id;
return item;
}
- private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, CancellationToken cancellationToken)
+ private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
{
+ var parentFolderId = parentFolder.Id;
+
BaseItem item;
bool isNew;
bool forceUpdate = false;
@@ -1226,66 +941,76 @@ namespace Emby.Server.Implementations.Channels
{
if (info.FolderType == ChannelFolderType.MusicAlbum)
{
- item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew);
}
else if (info.FolderType == ChannelFolderType.MusicArtist)
{
- item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew);
}
else if (info.FolderType == ChannelFolderType.PhotoAlbum)
{
- item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew);
}
else if (info.FolderType == ChannelFolderType.Series)
{
- item = GetItemById<Series>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew);
}
else if (info.FolderType == ChannelFolderType.Season)
{
- item = GetItemById<Season>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew);
}
else
{
- item = GetItemById<Folder>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew);
}
}
else if (info.MediaType == ChannelMediaType.Audio)
{
if (info.ContentType == ChannelMediaContentType.Podcast)
{
- item = GetItemById<AudioPodcast>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew);
}
else
{
- item = GetItemById<Audio>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Audio>(info.Id, channelProvider.Name, out isNew);
}
}
else
{
if (info.ContentType == ChannelMediaContentType.Episode)
{
- item = GetItemById<Episode>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew);
}
else if (info.ContentType == ChannelMediaContentType.Movie)
{
- item = GetItemById<Movie>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew);
}
else if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer)
{
- item = GetItemById<Trailer>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew);
}
else
{
- item = GetItemById<Video>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
+ item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew);
}
}
- item.RunTimeTicks = info.RunTimeTicks;
+ var enableMediaProbe = channelProvider is ISupportsMediaProbe;
+
+ if (info.IsLiveStream)
+ {
+ item.RunTimeTicks = null;
+ }
+
+ else if (isNew || !enableMediaProbe)
+ {
+ item.RunTimeTicks = info.RunTimeTicks;
+ }
if (isNew)
{
item.Name = info.Name;
- item.Genres = info.Genres;
+ item.Genres = info.Genres.ToArray();
item.Studios = info.Studios.ToArray(info.Studios.Count);
item.CommunityRating = info.CommunityRating;
item.Overview = info.Overview;
@@ -1297,7 +1022,7 @@ namespace Emby.Server.Implementations.Channels
item.OfficialRating = info.OfficialRating;
item.DateCreated = info.DateCreated ?? DateTime.UtcNow;
item.Tags = info.Tags.ToArray(info.Tags.Count);
- item.HomePageUrl = info.HomePageUrl;
+ item.OriginalTitle = info.OriginalTitle;
}
else if (info.Type == ChannelItemType.Folder && info.FolderType == ChannelFolderType.Container)
{
@@ -1326,22 +1051,56 @@ namespace Emby.Server.Implementations.Channels
{
if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes))
{
+ _logger.Debug("Forcing update due to TrailerTypes {0}", item.Name);
forceUpdate = true;
}
- trailer.TrailerTypes = info.TrailerTypes;
+ trailer.TrailerTypes = info.TrailerTypes.ToArray();
+ }
+
+ if (info.DateModified > item.DateModified)
+ {
+ item.DateModified = info.DateModified;
+ _logger.Debug("Forcing update due to DateModified {0}", item.Name);
+ forceUpdate = true;
}
- item.ChannelId = internalChannelId.ToString("N");
+ // was used for status
+ //if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
+ //{
+ // item.ExternalEtag = info.Etag;
+ // forceUpdate = true;
+ // _logger.Debug("Forcing update due to ExternalEtag {0}", item.Name);
+ //}
- if (item.ParentId != internalChannelId)
+ if (!internalChannelId.Equals(item.ChannelId))
{
forceUpdate = true;
+ _logger.Debug("Forcing update due to ChannelId {0}", item.Name);
+ }
+ item.ChannelId = internalChannelId;
+
+ if (!item.ParentId.Equals(parentFolderId))
+ {
+ forceUpdate = true;
+ _logger.Debug("Forcing update due to parent folder Id {0}", item.Name);
+ }
+ item.ParentId = parentFolderId;
+
+ var hasSeries = item as IHasSeriesName;
+ if (hasSeries != null)
+ {
+ if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase))
+ {
+ forceUpdate = true;
+ _logger.Debug("Forcing update due to SeriesName {0}", item.Name);
+ }
+ hasSeries.SeriesName = info.SeriesName;
}
- item.ParentId = internalChannelId;
if (!string.Equals(item.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase))
{
forceUpdate = true;
+ _logger.Debug("Forcing update due to ExternalId {0}", item.Name);
}
item.ExternalId = info.Id;
@@ -1363,20 +1122,41 @@ namespace Emby.Server.Implementations.Channels
item.Path = mediaSource == null ? null : mediaSource.Path;
}
- if (!string.IsNullOrWhiteSpace(info.ImageUrl) && !item.HasImage(ImageType.Primary))
+ if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary))
{
item.SetImagePath(ImageType.Primary, info.ImageUrl);
+ _logger.Debug("Forcing update due to ImageUrl {0}", item.Name);
+ forceUpdate = true;
+ }
+
+ if (!info.IsLiveStream)
+ {
+ if (item.Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase))
+ {
+ item.Tags = item.Tags.Except(new[] { "livestream" }, StringComparer.OrdinalIgnoreCase).ToArray();
+ _logger.Debug("Forcing update due to Tags {0}", item.Name);
+ forceUpdate = true;
+ }
+ }
+ else
+ {
+ if (!item.Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase))
+ {
+ item.Tags = item.Tags.Concat(new[] { "livestream" }).ToArray();
+ _logger.Debug("Forcing update due to Tags {0}", item.Name);
+ forceUpdate = true;
+ }
}
item.OnMetadataChanged();
if (isNew)
{
- _libraryManager.CreateItem(item, cancellationToken);
+ _libraryManager.CreateItem(item, parentFolder);
if (info.People != null && info.People.Count > 0)
{
- _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>());
+ _libraryManager.UpdatePeople(item, info.People);
}
}
else if (forceUpdate)
@@ -1384,27 +1164,24 @@ namespace Emby.Server.Implementations.Channels
item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
}
- SaveMediaSources(item, info.MediaSources);
-
- return item;
- }
-
- private void RefreshIfNeeded(BaseItem[] programs)
- {
- foreach (var program in programs)
+ if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)
{
- RefreshIfNeeded(program);
+ if (enableMediaProbe && !info.IsLiveStream && item.HasPathProtocol)
+ {
+ SaveMediaSources(item, new List<MediaSourceInfo>());
+ }
+ else
+ {
+ SaveMediaSources(item, info.MediaSources);
+ }
}
- }
- private void RefreshIfNeeded(BaseItem program)
- {
- if (!_refreshedItems.ContainsKey(program.Id))
+ if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime))
{
- _refreshedItems.TryAdd(program.Id, true);
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low);
+ _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Normal);
}
+ return item;
}
internal IChannel GetChannelProvider(Channel channel)
@@ -1415,7 +1192,7 @@ namespace Emby.Server.Implementations.Channels
}
var result = GetAllChannels()
- .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), channel.ChannelId, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase));
+ .FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(channel.ChannelId) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase));
if (result == null)
{
@@ -1425,15 +1202,10 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- internal IChannel GetChannelProvider(string internalChannelId)
+ internal IChannel GetChannelProvider(Guid internalChannelId)
{
- if (internalChannelId == null)
- {
- throw new ArgumentNullException("internalChannelId");
- }
-
var result = GetAllChannels()
- .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), internalChannelId, StringComparison.OrdinalIgnoreCase));
+ .FirstOrDefault(i => internalChannelId.Equals(GetInternalChannelId(i.Name)));
if (result == null)
{
@@ -1442,175 +1214,5 @@ namespace Emby.Server.Implementations.Channels
return result;
}
-
- private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user)
- {
- foreach (var filter in filters.OrderByDescending(f => (int)f))
- {
- items = ApplyFilter(items, filter, user);
- }
-
- return items;
- }
-
- private IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
-
- switch (filter)
- {
- case ItemFilter.IsFavoriteOrLikes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- if (userdata == null)
- {
- return false;
- }
-
- var likes = userdata.Likes ?? false;
- var favorite = userdata.IsFavorite;
-
- return likes || favorite;
- });
-
- case ItemFilter.Likes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
- });
-
- case ItemFilter.Dislikes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
- });
-
- case ItemFilter.IsFavorite:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.IsFavorite;
- });
-
- case ItemFilter.IsResumable:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.PlaybackPositionTicks > 0;
- });
-
- case ItemFilter.IsPlayed:
- return items.Where(item => item.IsPlayed(currentUser));
-
- case ItemFilter.IsUnplayed:
- return items.Where(item => item.IsUnplayed(currentUser));
-
- case ItemFilter.IsFolder:
- return items.Where(item => item.IsFolder);
-
- case ItemFilter.IsNotFolder:
- return items.Where(item => !item.IsFolder);
- }
-
- return items;
- }
-
- public BaseItemDto GetChannelFolder(string userId, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(userId);
-
- var folder = GetInternalChannelFolder(cancellationToken);
-
- return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
- }
-
- public Folder GetInternalChannelFolder(CancellationToken cancellationToken)
- {
- var name = _localization.GetLocalizedString("Channels");
-
- return _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken);
- }
- }
-
- public class ChannelsEntryPoint : IServerEntryPoint
- {
- private readonly IServerConfigurationManager _config;
- private readonly IChannelManager _channelManager;
- private readonly ITaskManager _taskManager;
- private readonly IFileSystem _fileSystem;
-
- public ChannelsEntryPoint(IChannelManager channelManager, ITaskManager taskManager, IServerConfigurationManager config, IFileSystem fileSystem)
- {
- _channelManager = channelManager;
- _taskManager = taskManager;
- _config = config;
- _fileSystem = fileSystem;
- }
-
- public void Run()
- {
- var channels = ((ChannelManager)_channelManager).Channels
- .Select(i => i.GetType().FullName.GetMD5().ToString("N"))
- .ToArray();
-
- var channelsString = string.Join(",", channels);
-
- if (!string.Equals(channelsString, GetSavedLastChannels(), StringComparison.OrdinalIgnoreCase))
- {
- _taskManager.QueueIfNotRunning<RefreshChannelsScheduledTask>();
-
- SetSavedLastChannels(channelsString);
- }
- }
-
- private string DataPath
- {
- get { return Path.Combine(_config.ApplicationPaths.DataPath, "channels.txt"); }
- }
-
- private string GetSavedLastChannels()
- {
- try
- {
- return _fileSystem.ReadAllText(DataPath);
- }
- catch
- {
- return string.Empty;
- }
- }
-
- private void SetSavedLastChannels(string value)
- {
- try
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- _fileSystem.DeleteFile(DataPath);
-
- }
- else
- {
- _fileSystem.WriteAllText(DataPath, value);
- }
- }
- catch
- {
- }
- }
-
- public void Dispose()
- {
- GC.SuppressFinalize(this);
- }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index ae31fcf3f..b211908d8 100644
--- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -1,15 +1,11 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Logging;
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Channels
{
@@ -28,35 +24,12 @@ namespace Emby.Server.Implementations.Channels
_libraryManager = libraryManager;
}
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+ public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var users = _userManager.Users
- .DistinctBy(GetUserDistinctValue)
- .Select(i => i.Id.ToString("N"))
- .ToList();
-
- var numComplete = 0;
-
- foreach (var user in users)
- {
- double percentPerUser = 1;
- percentPerUser /= users.Count;
- var startingPercent = numComplete * percentPerUser * 100;
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(startingPercent + percentPerUser * p));
-
- await DownloadContent(user, cancellationToken, innerProgress).ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= users.Count;
- progress.Report(percent * 100);
- }
-
- await CleanDatabase(cancellationToken).ConfigureAwait(false);
+ CleanDatabase(cancellationToken);
progress.Report(100);
+ return Task.CompletedTask;
}
public static string GetUserDistinctValue(User user)
@@ -68,60 +41,7 @@ namespace Emby.Server.Implementations.Channels
return string.Join("|", channels.ToArray());
}
- private async Task DownloadContent(string user, CancellationToken cancellationToken, IProgress<double> progress)
- {
- var channels = await _channelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = user
-
- }, cancellationToken);
-
- var numComplete = 0;
- var numItems = channels.Items.Length;
-
- foreach (var channel in channels.Items)
- {
- var channelId = channel.Id.ToString("N");
-
- var features = _channelManager.GetChannelFeatures(channelId);
-
- const int currentRefreshLevel = 1;
- var maxRefreshLevel = features.AutoRefreshLevels ?? 0;
- maxRefreshLevel = Math.Max(maxRefreshLevel, 2);
-
- if (maxRefreshLevel > 0)
- {
- var innerProgress = new ActionableProgress<double>();
-
- var startingNumberComplete = numComplete;
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = startingNumberComplete;
- innerPercent += p / 100;
- innerPercent /= numItems;
- progress.Report(innerPercent * 100);
- });
-
- try
- {
- await GetAllItems(user, channelId, null, currentRefreshLevel, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel content", ex);
- }
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private async Task CleanDatabase(CancellationToken cancellationToken)
+ private void CleanDatabase(CancellationToken cancellationToken)
{
var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
@@ -138,120 +58,45 @@ namespace Emby.Server.Implementations.Channels
{
cancellationToken.ThrowIfCancellationRequested();
- await CleanChannel(id, cancellationToken).ConfigureAwait(false);
+ CleanChannel(id, cancellationToken);
}
}
- private async Task CleanChannel(Guid id, CancellationToken cancellationToken)
+ private void CleanChannel(Guid id, CancellationToken cancellationToken)
{
_logger.Info("Cleaning channel {0} from database", id);
// Delete all channel items
var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
- ChannelIds = new[] { id.ToString("N") }
+ ChannelIds = new[] { id }
});
foreach (var deleteId in allIds)
{
cancellationToken.ThrowIfCancellationRequested();
- await DeleteItem(deleteId).ConfigureAwait(false);
+ DeleteItem(deleteId);
}
// Finally, delete the channel itself
- await DeleteItem(id).ConfigureAwait(false);
+ DeleteItem(id);
}
- private Task DeleteItem(Guid id)
+ private void DeleteItem(Guid id)
{
var item = _libraryManager.GetItemById(id);
if (item == null)
{
- return Task.FromResult(true);
+ return;
}
- return _libraryManager.DeleteItem(item, new DeleteOptions
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
- });
- }
-
- private async Task GetAllItems(string user, string channelId, string folderId, int currentRefreshLevel, int maxRefreshLevel, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var folderItems = new List<string>();
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(p / 2));
-
- var result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = channelId,
- UserId = user,
- FolderId = folderId
-
- }, innerProgress, cancellationToken);
-
- folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N")));
-
- var totalRetrieved = result.Items.Length;
- var totalCount = result.TotalRecordCount;
-
- while (totalRetrieved < totalCount)
- {
- result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = channelId,
- UserId = user,
- StartIndex = totalRetrieved,
- FolderId = folderId
-
- }, new SimpleProgress<double>(), cancellationToken);
- folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N")));
-
- totalRetrieved += result.Items.Length;
- totalCount = result.TotalRecordCount;
- }
-
- progress.Report(50);
-
- if (currentRefreshLevel < maxRefreshLevel)
- {
- var numComplete = 0;
- var numItems = folderItems.Count;
-
- foreach (var folder in folderItems)
- {
- try
- {
- innerProgress = new ActionableProgress<double>();
-
- var startingNumberComplete = numComplete;
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = startingNumberComplete;
- innerPercent += p / 100;
- innerPercent /= numItems;
- progress.Report(innerPercent * 50 + 50);
- });
-
- await GetAllItems(user, channelId, folder, currentRefreshLevel + 1, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel content", ex);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 50 + 50);
- }
- }
-
- progress.Report(100);
+ }, false);
}
}
}
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index f47e2d10a..858dada4b 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Images;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using System;
namespace Emby.Server.Implementations.Collections
{
@@ -21,7 +22,7 @@ namespace Emby.Server.Implementations.Collections
{
}
- protected override bool Supports(IHasMetadata item)
+ protected override bool Supports(BaseItem item)
{
// Right now this is the only way to prevent this image from getting created ahead of internet image providers
if (!item.IsLocked)
@@ -32,11 +33,11 @@ namespace Emby.Server.Implementations.Collections
return base.Supports(item);
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
var playlist = (BoxSet)item;
- var items = playlist.Children.Concat(playlist.GetLinkedChildren())
+ return playlist.Children.Concat(playlist.GetLinkedChildren())
.Select(i =>
{
var subItem = i;
@@ -57,7 +58,7 @@ namespace Emby.Server.Implementations.Collections
return subItem;
}
- var parent = subItem.IsOwnedItem ? subItem.GetOwner() : subItem.GetParent();
+ var parent = subItem.GetOwner() ?? subItem.GetParent();
if (parent != null && parent.HasImage(ImageType.Primary))
{
@@ -71,12 +72,11 @@ namespace Emby.Server.Implementations.Collections
})
.Where(i => i != null)
.DistinctBy(i => i.Id)
+ .OrderBy(i => Guid.NewGuid())
.ToList();
-
- return GetFinalItems(items, 2);
}
- protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(BaseItem 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 c8e947fd7..675a726e5 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -13,6 +13,12 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.Collections
{
@@ -23,36 +29,84 @@ namespace Emby.Server.Implementations.Collections
private readonly ILibraryMonitor _iLibraryMonitor;
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ private readonly ILocalizationManager _localizationManager;
+ private IApplicationPaths _appPaths;
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
- public CollectionManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger, IProviderManager providerManager)
+ public CollectionManager(ILibraryManager libraryManager, IApplicationPaths appPaths, ILocalizationManager localizationManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger, IProviderManager providerManager)
{
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_iLibraryMonitor = iLibraryMonitor;
_logger = logger;
_providerManager = providerManager;
+ _localizationManager = localizationManager;
+ _appPaths = appPaths;
}
- public Folder GetCollectionsFolder(string userId)
+ private IEnumerable<Folder> FindFolders(string path)
{
- return _libraryManager.RootFolder.Children.OfType<ManualCollectionsFolder>()
- .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<ManualCollectionsFolder>()
- .FirstOrDefault();
+ return _libraryManager
+ .RootFolder
+ .Children
+ .OfType<Folder>()
+ .Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
}
- public IEnumerable<BoxSet> GetCollections(User user)
+ internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
{
- var folder = GetCollectionsFolder(user.Id.ToString("N"));
+ var existingFolders = FindFolders(path)
+ .ToList();
+
+ if (existingFolders.Count > 0)
+ {
+ return existingFolders[0];
+ }
+
+ if (!createIfNeeded)
+ {
+ return null;
+ }
+
+ _fileSystem.CreateDirectory(path);
+
+ var libraryOptions = new LibraryOptions
+ {
+ PathInfos = new[] { new MediaPathInfo { Path = path } },
+ EnableRealtimeMonitor = false,
+ SaveLocalMetadata = true
+ };
+
+ var name = _localizationManager.GetLocalizedString("Collections");
+
+ await _libraryManager.AddVirtualFolder(name, CollectionType.BoxSets, libraryOptions, true).ConfigureAwait(false);
+
+ return FindFolders(path).First();
+ }
+
+ internal string GetCollectionsFolderPath()
+ {
+ return Path.Combine(_appPaths.DataPath, "collections");
+ }
+
+ private Task<Folder> GetCollectionsFolder(bool createIfNeeded)
+ {
+ return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
+ }
+
+ private IEnumerable<BoxSet> GetCollections(User user)
+ {
+ var folder = GetCollectionsFolder(false).Result;
+
return folder == null ?
new List<BoxSet>() :
folder.GetChildren(user, true).OfType<BoxSet>();
}
- public async Task<BoxSet> CreateCollection(CollectionCreationOptions options)
+ public BoxSet CreateCollection(CollectionCreationOptions options)
{
var name = options.Name;
@@ -61,7 +115,7 @@ namespace Emby.Server.Implementations.Collections
// This could cause it to get re-resolved as a plain folder
var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
- var parentFolder = GetParentFolder(options.ParentId);
+ var parentFolder = GetCollectionsFolder(true).Result;
if (parentFolder == null)
{
@@ -82,19 +136,14 @@ namespace Emby.Server.Implementations.Collections
Path = path,
IsLocked = options.IsLocked,
ProviderIds = options.ProviderIds,
- Shares = options.UserIds.Select(i => new Share
- {
- UserId = i,
- CanEdit = true
-
- }).ToList()
+ DateCreated = DateTime.UtcNow
};
parentFolder.AddChild(collection, CancellationToken.None);
if (options.ItemIdList.Length > 0)
{
- await AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(_fileSystem)
+ AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(_fileSystem)
{
// The initial adding of items is going to create a local metadata file
// This will cause internet metadata to be skipped as a result
@@ -122,44 +171,17 @@ namespace Emby.Server.Implementations.Collections
}
}
- private Folder GetParentFolder(Guid? parentId)
+ public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
{
- if (parentId.HasValue)
- {
- if (parentId.Value == Guid.Empty)
- {
- throw new ArgumentNullException("parentId");
- }
-
- var folder = _libraryManager.GetItemById(parentId.Value) as Folder;
-
- // Find an actual physical folder
- if (folder is CollectionFolder)
- {
- var child = _libraryManager.RootFolder.Children.OfType<Folder>()
- .FirstOrDefault(i => folder.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
-
- if (child != null)
- {
- return child;
- }
- }
- }
-
- return GetCollectionsFolder(string.Empty);
- }
-
- public Task AddToCollection(Guid collectionId, IEnumerable<string> ids)
- {
- return AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(_fileSystem));
+ AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(_fileSystem));
}
- public Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
+ public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
{
- return AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(_fileSystem));
+ AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(_fileSystem));
}
- private async Task AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
+ private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -170,28 +192,26 @@ namespace Emby.Server.Implementations.Collections
var list = new List<LinkedChild>();
var itemList = new List<BaseItem>();
- var currentLinkedChildrenIds = collection.GetLinkedChildren().Select(i => i.Id).ToList();
+
+ var linkedChildrenList = collection.GetLinkedChildren();
+ var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList();
foreach (var id in ids)
{
var guidId = new Guid(id);
var item = _libraryManager.GetItemById(guidId);
- if (string.IsNullOrWhiteSpace(item.Path))
- {
- continue;
- }
-
if (item == null)
{
throw new ArgumentException("No item exists with the supplied Id");
}
- itemList.Add(item);
-
if (!currentLinkedChildrenIds.Contains(guidId))
{
+ itemList.Add(item);
+
list.Add(LinkedChild.Create(item));
+ linkedChildrenList.Add(item);
}
}
@@ -201,10 +221,11 @@ namespace Emby.Server.Implementations.Collections
newList.AddRange(list);
collection.LinkedChildren = newList.ToArray(newList.Count);
- collection.UpdateRatingToContent();
+ collection.UpdateRatingToItems(linkedChildrenList);
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ refreshOptions.ForceSave = true;
_providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
if (fireEvent)
@@ -219,12 +240,12 @@ namespace Emby.Server.Implementations.Collections
}
}
- public Task RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
+ public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
{
- return RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
+ RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
}
- public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
+ public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -244,7 +265,8 @@ namespace Emby.Server.Implementations.Collections
if (child == null)
{
- throw new ArgumentException("No collection title exists with the supplied Id");
+ _logger.Warn("No collection title exists with the supplied Id");
+ continue;
}
list.Add(child);
@@ -260,10 +282,11 @@ namespace Emby.Server.Implementations.Collections
collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
}
- collection.UpdateRatingToContent();
-
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
- _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.High);
+ _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)
+ {
+ ForceSave = true
+ }, RefreshPriority.High);
EventHelper.FireEventIfNotNull(ItemsRemovedFromCollection, this, new CollectionModifiedEventArgs
{
@@ -292,7 +315,7 @@ namespace Emby.Server.Implementations.Collections
var itemId = item.Id;
var currentBoxSets = allBoxsets
- .Where(i => i.GetLinkedChildren().Any(j => j.Id == itemId))
+ .Where(i => i.ContainsLinkedChildByItemId(itemId))
.ToList();
if (currentBoxSets.Count > 0)
@@ -312,4 +335,78 @@ namespace Emby.Server.Implementations.Collections
return results.Values;
}
}
+
+ public class CollectionManagerEntryPoint : IServerEntryPoint
+ {
+ private readonly CollectionManager _collectionManager;
+ private readonly IServerConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
+ private ILogger _logger;
+
+ public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ {
+ _collectionManager = (CollectionManager)collectionManager;
+ _config = config;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ }
+
+ public async void Run()
+ {
+ if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
+ {
+ var path = _collectionManager.GetCollectionsFolderPath();
+
+ if (_fileSystem.DirectoryExists(path))
+ {
+ try
+ {
+ await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error creating camera uploads library", ex);
+ }
+
+ _config.Configuration.CollectionsUpgraded = true;
+ _config.SaveConfiguration();
+ }
+ }
+ }
+
+ #region IDisposable Support
+ private bool disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: dispose managed state (managed objects).
+ }
+
+ // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+ // TODO: set large fields to null.
+
+ disposedValue = true;
+ }
+ }
+
+ // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
+ // ~CollectionManagerEntryPoint() {
+ // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ // Dispose(false);
+ // }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
}
diff --git a/Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs
deleted file mode 100644
index c7bcdfe25..000000000
--- a/Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using System.IO;
-
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.IO;
-
-namespace Emby.Server.Implementations.Collections
-{
- public class CollectionsDynamicFolder : IVirtualFolderCreator
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
-
- public CollectionsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem)
- {
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- }
-
- public BasePluginFolder GetFolder()
- {
- var path = Path.Combine(_appPaths.DataPath, "collections");
-
- _fileSystem.CreateDirectory(path);
-
- return new ManualCollectionsFolder
- {
- Path = path
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index e73a69892..47e2ec0a8 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -102,8 +102,6 @@ namespace Emby.Server.Implementations.Configuration
}
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
-
- ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath;
}
private string GetInternalMetadataPath()
@@ -237,6 +235,18 @@ namespace Emby.Server.Implementations.Configuration
changed = true;
}
+ if (!config.CameraUploadUpgraded)
+ {
+ config.CameraUploadUpgraded = true;
+ changed = true;
+ }
+
+ if (!config.CollectionsUpgraded)
+ {
+ config.CollectionsUpgraded = true;
+ changed = true;
+ }
+
return changed;
}
}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index d207c8d4f..76ebff3a8 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -189,6 +189,26 @@ namespace Emby.Server.Implementations.Data
return sql.Select(connection.PrepareStatement).ToList();
}
+ protected bool TableExists(ManagedConnection connection, string name)
+ {
+ return connection.RunInTransaction(db =>
+ {
+ using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master"))
+ {
+ foreach (var row in statement.ExecuteQuery())
+ {
+ if (string.Equals(name, row.GetString(0), StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ }, ReadTransactionMode);
+ }
+
protected void RunDefaultInitialization(ManagedConnection db)
{
var queries = new List<string>
@@ -264,7 +284,6 @@ namespace Emby.Server.Implementations.Data
{
_disposed = true;
Dispose(true);
- GC.SuppressFinalize(this);
}
private readonly object _disposeLock = new object();
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 37ba2eb5f..8611cabc1 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -33,10 +33,11 @@ namespace Emby.Server.Implementations.Data
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return CleanDeadItems(cancellationToken, progress);
+ CleanDeadItems(cancellationToken, progress);
+ return Task.CompletedTask;
}
- private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
+ private void CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
{
var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
@@ -58,11 +59,11 @@ namespace Emby.Server.Implementations.Data
{
_logger.Info("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
- await item.Delete(new DeleteOptions
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
- }).ConfigureAwait(false);
+ });
}
numComplete++;
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
index 5d0fc8ebc..91a2dfdf6 100644
--- a/Emby.Server.Implementations/Data/ManagedConnection.cs
+++ b/Emby.Server.Implementations/Data/ManagedConnection.cs
@@ -77,7 +77,6 @@ namespace Emby.Server.Implementations.Data
{
Close();
}
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index e6afcd410..09ff7e09d 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -18,14 +18,12 @@ namespace Emby.Server.Implementations.Data
/// </summary>
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
- private readonly IMemoryStreamFactory _memoryStreamProvider;
protected IFileSystem FileSystem { get; private set; }
- public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider, IFileSystem fileSystem)
+ public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem)
: base(logger)
{
_jsonSerializer = jsonSerializer;
- _memoryStreamProvider = memoryStreamProvider;
FileSystem = fileSystem;
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
@@ -98,7 +96,7 @@ namespace Emby.Server.Implementations.Data
{
throw new ArgumentNullException("displayPreferences");
}
- if (string.IsNullOrWhiteSpace(displayPreferences.Id))
+ if (string.IsNullOrEmpty(displayPreferences.Id))
{
throw new ArgumentNullException("displayPreferences.Id");
}
@@ -119,7 +117,7 @@ namespace Emby.Server.Implementations.Data
private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
{
- var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider);
+ var serialized = _jsonSerializer.SerializeToBytes(displayPreferences);
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
{
@@ -174,7 +172,7 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="System.ArgumentNullException">item</exception>
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
{
- if (string.IsNullOrWhiteSpace(displayPreferencesId))
+ if (string.IsNullOrEmpty(displayPreferencesId))
{
throw new ArgumentNullException("displayPreferencesId");
}
@@ -236,7 +234,7 @@ namespace Emby.Server.Implementations.Data
private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
{
- using (var stream = _memoryStreamProvider.CreateNew(row[0].ToBlob()))
+ using (var stream = new MemoryStream(row[0].ToBlob()))
{
stream.Position = 0;
return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index d2c851b3c..a755c65f4 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using SQLitePCL.pretty;
+using System.IO;
namespace Emby.Server.Implementations.Data
{
@@ -110,19 +111,33 @@ namespace Emby.Server.Implementations.Data
DateTimeStyles.None).ToUniversalTime();
}
+ public static DateTime? TryReadDateTime(this IResultSetValue result)
+ {
+ var dateText = result.ToString();
+
+ DateTime dateTimeResult;
+
+ if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out dateTimeResult))
+ {
+ return dateTimeResult.ToUniversalTime();
+ }
+
+ return null;
+ }
+
/// <summary>
/// Serializes to bytes.
/// </summary>
/// <returns>System.Byte[][].</returns>
/// <exception cref="System.ArgumentNullException">obj</exception>
- public static byte[] SerializeToBytes(this IJsonSerializer json, object obj, IMemoryStreamFactory streamProvider)
+ public static byte[] SerializeToBytes(this IJsonSerializer json, object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
- using (var stream = streamProvider.CreateNew())
+ using (var stream = new MemoryStream())
{
json.SerializeToStream(obj, stream);
return stream.ToArray();
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 830d6447e..bde28c923 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -32,6 +32,9 @@ using SQLitePCL.pretty;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.Data
{
@@ -65,18 +68,16 @@ namespace Emby.Server.Implementations.Data
/// </summary>
private readonly IServerConfigurationManager _config;
- private readonly string _criticReviewsPath;
-
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly IFileSystem _fileSystem;
private readonly IEnvironmentInfo _environmentInfo;
- private readonly ITimerFactory _timerFactory;
- private ITimer _shrinkMemoryTimer;
+ private IServerApplicationHost _appHost;
+
+ public IImageProcessor ImageProcessor { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
- public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, IAssemblyInfo assemblyInfo, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, ITimerFactory timerFactory)
+ public SqliteItemRepository(IServerConfigurationManager config, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, ILogger logger, IAssemblyInfo assemblyInfo, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, ITimerFactory timerFactory)
: base(logger)
{
if (config == null)
@@ -88,15 +89,13 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException("jsonSerializer");
}
+ _appHost = appHost;
_config = config;
_jsonSerializer = jsonSerializer;
- _memoryStreamProvider = memoryStreamProvider;
_fileSystem = fileSystem;
_environmentInfo = environmentInfo;
- _timerFactory = timerFactory;
_typeMapper = new TypeMapper(assemblyInfo);
- _criticReviewsPath = Path.Combine(_config.ApplicationPaths.DataPath, "critic-reviews");
DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
}
@@ -118,28 +117,17 @@ namespace Emby.Server.Implementations.Data
}
}
- protected override void CloseConnection()
- {
- if (_shrinkMemoryTimer != null)
- {
- _shrinkMemoryTimer.Dispose();
- _shrinkMemoryTimer = null;
- }
-
- base.CloseConnection();
- }
-
/// <summary>
/// Opens the connection to the database
/// </summary>
- public void Initialize(SqliteUserDataRepository userDataRepo)
+ public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
{
using (var connection = CreateConnection())
{
RunDefaultInitialization(connection);
var createMediaStreamsTableCommand
- = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
string[] queries = {
"PRAGMA locking_mode=EXCLUSIVE",
@@ -162,8 +150,6 @@ namespace Emby.Server.Implementations.Data
createMediaStreamsTableCommand,
- "create index if not exists idx_mediastreams1 on mediastreams(ItemId)",
-
"pragma shrink_memory"
};
@@ -182,8 +168,6 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "EndDate", "DATETIME", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ChannelId", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IsMovie", "BIT", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "IsSports", "BIT", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "IsKids", "BIT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "CommunityRating", "Float", existingColumnNames);
AddColumn(db, "TypedBaseItems", "CustomRating", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "IndexNumber", "INT", existingColumnNames);
@@ -200,19 +184,13 @@ namespace Emby.Server.Implementations.Data
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", "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);
@@ -244,8 +222,7 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "ProviderIds", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Images", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ProductionLocations", "Text", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "ThemeSongIds", "Text", existingColumnNames);
- AddColumn(db, "TypedBaseItems", "ThemeVideoIds", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExtraIds", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "TotalBitrate", "INT", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ExtraType", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "Artists", "Text", existingColumnNames);
@@ -254,6 +231,8 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "TypedBaseItems", "SeriesPresentationUniqueKey", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "ShowId", "Text", existingColumnNames);
AddColumn(db, "TypedBaseItems", "OwnerId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Width", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Height", "INT", existingColumnNames);
existingColumnNames = GetColumnNames(db, "ItemValues");
AddColumn(db, "ItemValues", "CleanValue", "Text", existingColumnNames);
@@ -274,6 +253,11 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "MediaStreams", "RefFrames", "INT", existingColumnNames);
AddColumn(db, "MediaStreams", "KeyFrames", "TEXT", existingColumnNames);
AddColumn(db, "MediaStreams", "IsAnamorphic", "BIT", existingColumnNames);
+
+ AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames);
+
}, TransactionMode);
string[] postQueries =
@@ -282,6 +266,7 @@ namespace Emby.Server.Implementations.Data
// obsolete
"drop index if exists idx_TypedBaseItems",
"drop index if exists idx_mediastreams",
+ "drop index if exists idx_mediastreams1",
"drop index if exists idx_"+ChaptersTableName,
"drop index if exists idx_UserDataKeys1",
"drop index if exists idx_UserDataKeys2",
@@ -316,7 +301,6 @@ namespace Emby.Server.Implementations.Data
"create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
"create index if not exists idx_GuidTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,Type,IsFolder,IsVirtualItem)",
- //"create index if not exists idx_GuidMediaTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,MediaType,IsFolder,IsVirtualItem)",
"create index if not exists idx_CleanNameType on TypedBaseItems(CleanName,Type)",
// covering index
@@ -359,32 +343,7 @@ namespace Emby.Server.Implementations.Data
//await Vacuum(_connection).ConfigureAwait(false);
}
- userDataRepo.Initialize(WriteLock, _connection);
-
- _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30));
- }
-
- private void OnShrinkMemoryTimerCallback(object state)
- {
- try
- {
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunQueries(new string[]
- {
- "pragma shrink_memory"
- });
- }
- }
-
- GC.Collect();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error running shrink memory", ex);
- }
+ userDataRepo.Initialize(WriteLock, _connection, userManager);
}
private readonly string[] _retriveItemColumns =
@@ -395,12 +354,7 @@ namespace Emby.Server.Implementations.Data
"EndDate",
"ChannelId",
"IsMovie",
- "IsSports",
- "IsKids",
"IsSeries",
- "IsLive",
- "IsNews",
- "IsPremiere",
"EpisodeTitle",
"IsRepeat",
"CommunityRating",
@@ -409,8 +363,8 @@ namespace Emby.Server.Implementations.Data
"IsLocked",
"PreferredMetadataLanguage",
"PreferredMetadataCountryCode",
- "IsHD",
- "ExternalEtag",
+ "Width",
+ "Height",
"DateLastRefreshed",
"Name",
"Path",
@@ -419,7 +373,6 @@ namespace Emby.Server.Implementations.Data
"ParentIndexNumber",
"ProductionYear",
"OfficialRating",
- "HomePageUrl",
"ForcedSortName",
"RunTimeTicks",
"DateCreated",
@@ -452,8 +405,7 @@ namespace Emby.Server.Implementations.Data
"ProviderIds",
"Images",
"ProductionLocations",
- "ThemeSongIds",
- "ThemeVideoIds",
+ "ExtraIds",
"TotalBitrate",
"ExtraType",
"Artists",
@@ -497,7 +449,10 @@ namespace Emby.Server.Implementations.Data
"IsAvc",
"Title",
"TimeBase",
- "CodecTimeBase"
+ "CodecTimeBase",
+ "ColorPrimaries",
+ "ColorSpace",
+ "ColorTransfer"
};
private string GetSaveItemCommandText()
@@ -511,13 +466,8 @@ namespace Emby.Server.Implementations.Data
"StartDate",
"EndDate",
"ChannelId",
- "IsKids",
"IsMovie",
- "IsSports",
"IsSeries",
- "IsLive",
- "IsNews",
- "IsPremiere",
"EpisodeTitle",
"IsRepeat",
"CommunityRating",
@@ -537,13 +487,12 @@ namespace Emby.Server.Implementations.Data
"SortName",
"ForcedSortName",
"RunTimeTicks",
- "HomePageUrl",
"DateCreated",
"DateModified",
"PreferredMetadataLanguage",
"PreferredMetadataCountryCode",
- "IsHD",
- "ExternalEtag",
+ "Width",
+ "Height",
"DateLastRefreshed",
"DateLastSaved",
"IsInMixedFolder",
@@ -574,8 +523,7 @@ namespace Emby.Server.Implementations.Data
"ProviderIds",
"Images",
"ProductionLocations",
- "ThemeSongIds",
- "ThemeVideoIds",
+ "ExtraIds",
"TotalBitrate",
"ExtraType",
"Artists",
@@ -699,46 +647,58 @@ namespace Emby.Server.Implementations.Data
var statements = PrepareAllSafe(db, new string[]
{
GetSaveItemCommandText(),
- "delete from AncestorIds where ItemId=@ItemId",
- "insert into AncestorIds (ItemId, AncestorId, AncestorIdText) values (@ItemId, @AncestorId, @AncestorIdText)"
+ "delete from AncestorIds where ItemId=@ItemId"
+
}).ToList();
using (var saveItemStatement = statements[0])
{
using (var deleteAncestorsStatement = statements[1])
{
- using (var updateAncestorsStatement = statements[2])
+ foreach (var tuple in tuples)
{
- foreach (var tuple in tuples)
+ if (requiresReset)
{
- if (requiresReset)
- {
- saveItemStatement.Reset();
- }
+ saveItemStatement.Reset();
+ }
- var item = tuple.Item1;
- var topParent = tuple.Item3;
- var userDataKey = tuple.Item4;
+ var item = tuple.Item1;
+ var topParent = tuple.Item3;
+ var userDataKey = tuple.Item4;
- SaveItem(item, topParent, userDataKey, saveItemStatement);
- //Logger.Debug(_saveItemCommand.CommandText);
+ SaveItem(item, topParent, userDataKey, saveItemStatement);
+ //Logger.Debug(_saveItemCommand.CommandText);
- var inheritedTags = tuple.Item5;
+ var inheritedTags = tuple.Item5;
- if (item.SupportsAncestors)
- {
- UpdateAncestors(item.Id, tuple.Item2, db, deleteAncestorsStatement, updateAncestorsStatement);
- }
+ if (item.SupportsAncestors)
+ {
+ UpdateAncestors(item.Id, tuple.Item2, db, deleteAncestorsStatement);
+ }
- UpdateItemValues(item.Id, GetItemValuesToSave(item, inheritedTags), db);
+ UpdateItemValues(item.Id, GetItemValuesToSave(item, inheritedTags), db);
- requiresReset = true;
- }
+ requiresReset = true;
}
}
}
}
+ private string GetPathToSave(string path)
+ {
+ if (path == null)
+ {
+ return null;
+ }
+
+ return _appHost.ReverseVirtualPath(path);
+ }
+
+ private string RestorePath(string path)
+ {
+ return _appHost.ExpandVirtualPath(path);
+ }
+
private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, IStatement saveItemStatement)
{
saveItemStatement.TryBind("@guid", item.Id);
@@ -746,14 +706,14 @@ namespace Emby.Server.Implementations.Data
if (TypeRequiresDeserialization(item.GetType()))
{
- saveItemStatement.TryBind("@data", _jsonSerializer.SerializeToBytes(item, _memoryStreamProvider));
+ saveItemStatement.TryBind("@data", _jsonSerializer.SerializeToBytes(item));
}
else
{
saveItemStatement.TryBindNull("@data");
}
- saveItemStatement.TryBind("@Path", item.Path);
+ saveItemStatement.TryBind("@Path", GetPathToSave(item.Path));
var hasStartDate = item as IHasStartDate;
if (hasStartDate != null)
@@ -774,30 +734,20 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@EndDate");
}
- saveItemStatement.TryBind("@ChannelId", item.ChannelId);
+ saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N"));
var hasProgramAttributes = item as IHasProgramAttributes;
if (hasProgramAttributes != null)
{
- saveItemStatement.TryBind("@IsKids", hasProgramAttributes.IsKids);
saveItemStatement.TryBind("@IsMovie", hasProgramAttributes.IsMovie);
- saveItemStatement.TryBind("@IsSports", hasProgramAttributes.IsSports);
saveItemStatement.TryBind("@IsSeries", hasProgramAttributes.IsSeries);
- saveItemStatement.TryBind("@IsLive", hasProgramAttributes.IsLive);
- saveItemStatement.TryBind("@IsNews", hasProgramAttributes.IsNews);
- saveItemStatement.TryBind("@IsPremiere", hasProgramAttributes.IsPremiere);
saveItemStatement.TryBind("@EpisodeTitle", hasProgramAttributes.EpisodeTitle);
saveItemStatement.TryBind("@IsRepeat", hasProgramAttributes.IsRepeat);
}
else
{
- saveItemStatement.TryBindNull("@IsKids");
saveItemStatement.TryBindNull("@IsMovie");
- saveItemStatement.TryBindNull("@IsSports");
saveItemStatement.TryBindNull("@IsSeries");
- saveItemStatement.TryBindNull("@IsLive");
- saveItemStatement.TryBindNull("@IsNews");
- saveItemStatement.TryBindNull("@IsPremiere");
saveItemStatement.TryBindNull("@EpisodeTitle");
saveItemStatement.TryBindNull("@IsRepeat");
}
@@ -815,7 +765,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ProductionYear", item.ProductionYear);
var parentId = item.ParentId;
- if (parentId == Guid.Empty)
+ if (parentId.Equals(Guid.Empty))
{
saveItemStatement.TryBindNull("@ParentId");
}
@@ -824,9 +774,9 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ParentId", parentId);
}
- if (item.Genres.Count > 0)
+ if (item.Genres.Length > 0)
{
- saveItemStatement.TryBind("@Genres", string.Join("|", item.Genres.ToArray()));
+ saveItemStatement.TryBind("@Genres", string.Join("|", item.Genres));
}
else
{
@@ -841,14 +791,28 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@RunTimeTicks", item.RunTimeTicks);
- saveItemStatement.TryBind("@HomePageUrl", item.HomePageUrl);
saveItemStatement.TryBind("@DateCreated", item.DateCreated);
saveItemStatement.TryBind("@DateModified", item.DateModified);
saveItemStatement.TryBind("@PreferredMetadataLanguage", item.PreferredMetadataLanguage);
saveItemStatement.TryBind("@PreferredMetadataCountryCode", item.PreferredMetadataCountryCode);
- saveItemStatement.TryBind("@IsHD", item.IsHD);
- saveItemStatement.TryBind("@ExternalEtag", item.ExternalEtag);
+
+ if (item.Width > 0)
+ {
+ saveItemStatement.TryBind("@Width", item.Width);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Width");
+ }
+ if (item.Height > 0)
+ {
+ saveItemStatement.TryBind("@Height", item.Height);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Height");
+ }
if (item.DateLastRefreshed != default(DateTime))
{
@@ -897,7 +861,15 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@Audio");
}
- saveItemStatement.TryBind("@ExternalServiceId", item.ServiceName);
+ var livetvChannel = item as LiveTvChannel;
+ if (livetvChannel != null)
+ {
+ saveItemStatement.TryBind("@ExternalServiceId", livetvChannel.ServiceName);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@ExternalServiceId");
+ }
if (item.Tags.Length > 0)
{
@@ -924,7 +896,7 @@ namespace Emby.Server.Implementations.Data
}
var trailer = item as Trailer;
- if (trailer != null && trailer.TrailerTypes.Count > 0)
+ if (trailer != null && trailer.TrailerTypes.Length > 0)
{
saveItemStatement.TryBind("@TrailerTypes", string.Join("|", trailer.TrailerTypes.Select(i => i.ToString()).ToArray()));
}
@@ -970,10 +942,10 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@Album", item.Album);
saveItemStatement.TryBind("@IsVirtualItem", item.IsVirtualItem);
- var hasSeries = item as IHasSeries;
- if (hasSeries != null)
+ var hasSeriesName = item as IHasSeriesName;
+ if (hasSeriesName != null)
{
- saveItemStatement.TryBind("@SeriesName", hasSeries.SeriesName);
+ saveItemStatement.TryBind("@SeriesName", hasSeriesName.SeriesName);
}
else
{
@@ -993,7 +965,10 @@ namespace Emby.Server.Implementations.Data
if (episode != null)
{
saveItemStatement.TryBind("@SeasonName", episode.SeasonName);
- saveItemStatement.TryBind("@SeasonId", episode.SeasonId);
+
+ var nullableSeasonId = episode.SeasonId.Equals(Guid.Empty) ? (Guid?)null : episode.SeasonId;
+
+ saveItemStatement.TryBind("@SeasonId", nullableSeasonId);
}
else
{
@@ -1001,9 +976,12 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@SeasonId");
}
+ var hasSeries = item as IHasSeries;
if (hasSeries != null)
{
- saveItemStatement.TryBind("@SeriesId", hasSeries.SeriesId);
+ var nullableSeriesId = hasSeries.SeriesId.Equals(Guid.Empty) ? (Guid?)null : hasSeries.SeriesId;
+
+ saveItemStatement.TryBind("@SeriesId", nullableSeriesId);
saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey);
}
else
@@ -1027,22 +1005,13 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@ProductionLocations");
}
- if (item.ThemeSongIds.Length > 0)
+ if (item.ExtraIds.Length > 0)
{
- saveItemStatement.TryBind("@ThemeSongIds", string.Join("|", item.ThemeSongIds.ToArray()));
+ saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds.ToArray()));
}
else
{
- saveItemStatement.TryBindNull("@ThemeSongIds");
- }
-
- if (item.ThemeVideoIds.Length > 0)
- {
- saveItemStatement.TryBind("@ThemeVideoIds", string.Join("|", item.ThemeVideoIds.ToArray()));
- }
- else
- {
- saveItemStatement.TryBindNull("@ThemeVideoIds");
+ saveItemStatement.TryBindNull("@ExtraIds");
}
saveItemStatement.TryBind("@TotalBitrate", item.TotalBitrate);
@@ -1089,7 +1058,7 @@ namespace Emby.Server.Implementations.Data
}
var ownerId = item.OwnerId;
- if (ownerId != Guid.Empty)
+ if (!ownerId.Equals(Guid.Empty))
{
saveItemStatement.TryBind("@OwnerId", ownerId);
}
@@ -1193,7 +1162,7 @@ namespace Emby.Server.Implementations.Data
path = string.Empty;
}
- return path +
+ return GetPathToSave(path) +
delimeter +
image.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) +
delimeter +
@@ -1215,7 +1184,7 @@ namespace Emby.Server.Implementations.Data
var image = new ItemImageInfo();
- image.Path = parts[0];
+ image.Path = RestorePath(parts[0]);
long ticks;
if (long.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
@@ -1255,7 +1224,7 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="System.ArgumentException"></exception>
public BaseItem RetrieveItem(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -1324,15 +1293,6 @@ namespace Emby.Server.Implementations.Data
{
return false;
}
-
- if (type == typeof(ManualCollectionsFolder))
- {
- return false;
- }
- if (type == typeof(CameraUploadsFolder))
- {
- return false;
- }
if (type == typeof(PlaylistsFolder))
{
return false;
@@ -1351,22 +1311,10 @@ namespace Emby.Server.Implementations.Data
{
return false;
}
- if (type == typeof(RecordingGroup))
- {
- return false;
- }
if (type == typeof(LiveTvProgram))
{
return false;
}
- if (type == typeof(LiveTvAudioRecording))
- {
- return false;
- }
- if (type == typeof(AudioPodcast))
- {
- return false;
- }
if (type == typeof(AudioBook))
{
return false;
@@ -1386,10 +1334,10 @@ namespace Emby.Server.Implementations.Data
private BaseItem GetItem(IReadOnlyList<IResultSetValue> reader, InternalItemsQuery query)
{
- return GetItem(reader, query, HasProgramAttributes(query), HasEpisodeAttributes(query), HasStartDate(query), HasTrailerTypes(query), HasArtistFields(query), HasSeriesFields(query));
+ return GetItem(reader, query, HasProgramAttributes(query), HasEpisodeAttributes(query), HasServiceName(query), HasStartDate(query), HasTrailerTypes(query), HasArtistFields(query), HasSeriesFields(query));
}
- private BaseItem GetItem(IReadOnlyList<IResultSetValue> reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields)
+ private BaseItem GetItem(IReadOnlyList<IResultSetValue> reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool hasServiceName, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields)
{
var typeString = reader.GetString(0);
@@ -1406,7 +1354,7 @@ namespace Emby.Server.Implementations.Data
if (TypeRequiresDeserialization(type))
{
- using (var stream = _memoryStreamProvider.CreateNew(reader[1].ToBlob()))
+ using (var stream = new MemoryStream(reader[1].ToBlob()))
{
stream.Position = 0;
@@ -1454,13 +1402,13 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- item.EndDate = reader[index].ReadDateTime();
+ item.EndDate = reader[index].TryReadDateTime();
}
index++;
if (!reader.IsDBNull(index))
{
- item.ChannelId = reader.GetString(index);
+ item.ChannelId = new Guid(reader.GetString(index));
}
index++;
@@ -1477,42 +1425,12 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsSports = reader.GetBoolean(index);
- }
- index++;
-
- if (!reader.IsDBNull(index))
- {
- hasProgramAttributes.IsKids = reader.GetBoolean(index);
- }
- index++;
-
- if (!reader.IsDBNull(index))
- {
hasProgramAttributes.IsSeries = reader.GetBoolean(index);
}
index++;
if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsLive = reader.GetBoolean(index);
- }
- index++;
-
- if (!reader.IsDBNull(index))
- {
- hasProgramAttributes.IsNews = reader.GetBoolean(index);
- }
- index++;
-
- if (!reader.IsDBNull(index))
- {
- hasProgramAttributes.IsPremiere = reader.GetBoolean(index);
- }
- index++;
-
- if (!reader.IsDBNull(index))
- {
hasProgramAttributes.EpisodeTitle = reader.GetString(index);
}
index++;
@@ -1525,7 +1443,7 @@ namespace Emby.Server.Implementations.Data
}
else
{
- index += 9;
+ index += 4;
}
}
@@ -1571,17 +1489,20 @@ namespace Emby.Server.Implementations.Data
index++;
}
- if (!reader.IsDBNull(index))
+ if (HasField(query, ItemFields.Width))
{
- item.IsHD = reader.GetBoolean(index);
+ if (!reader.IsDBNull(index))
+ {
+ item.Width = reader.GetInt32(index);
+ }
+ index++;
}
- index++;
- if (HasField(query, ItemFields.ExternalEtag))
+ if (HasField(query, ItemFields.Height))
{
if (!reader.IsDBNull(index))
{
- item.ExternalEtag = reader.GetString(index);
+ item.Height = reader.GetInt32(index);
}
index++;
}
@@ -1603,13 +1524,13 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- item.Path = reader.GetString(index);
+ item.Path = RestorePath(reader.GetString(index));
}
index++;
if (!reader.IsDBNull(index))
{
- item.PremiereDate = reader[index].ReadDateTime();
+ item.PremiereDate = reader[index].TryReadDateTime();
}
index++;
@@ -1640,15 +1561,6 @@ namespace Emby.Server.Implementations.Data
}
index++;
- if (HasField(query, ItemFields.HomePageUrl))
- {
- if (!reader.IsDBNull(index))
- {
- item.HomePageUrl = reader.GetString(index);
- }
- index++;
- }
-
if (HasField(query, ItemFields.SortName))
{
if (!reader.IsDBNull(index))
@@ -1686,7 +1598,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.Genres = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ item.Genres = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1709,11 +1621,18 @@ namespace Emby.Server.Implementations.Data
// TODO: Even if not needed by apps, the server needs it internally
// But get this excluded from contexts where it is not needed
- if (!reader.IsDBNull(index))
+ if (hasServiceName)
{
- item.ServiceName = reader.GetString(index);
+ var livetvChannel = item as LiveTvChannel;
+ if (livetvChannel != null)
+ {
+ if (!reader.IsDBNull(index))
+ {
+ livetvChannel.ServiceName = reader.GetString(index);
+ }
+ }
+ index++;
}
- index++;
if (!reader.IsDBNull(index))
{
@@ -1785,7 +1704,7 @@ namespace Emby.Server.Implementations.Data
}
return (TrailerType?)null;
- }).Where(i => i.HasValue).Select(i => i.Value).ToList();
+ }).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
}
index++;
@@ -1815,7 +1734,7 @@ namespace Emby.Server.Implementations.Data
var folder = item as Folder;
if (folder != null && !reader.IsDBNull(index))
{
- folder.DateLastMediaAdded = reader[index].ReadDateTime();
+ folder.DateLastMediaAdded = reader[index].TryReadDateTime();
}
index++;
}
@@ -1838,18 +1757,15 @@ namespace Emby.Server.Implementations.Data
}
index++;
- var hasSeries = item as IHasSeries;
- if (hasSeriesFields)
+ var hasSeriesName = item as IHasSeriesName;
+ if (hasSeriesName != null)
{
- if (hasSeries != null)
+ if (!reader.IsDBNull(index))
{
- if (!reader.IsDBNull(index))
- {
- hasSeries.SeriesName = reader.GetString(index);
- }
+ hasSeriesName.SeriesName = reader.GetString(index);
}
- index++;
}
+ index++;
if (hasEpisodeAttributes)
{
@@ -1873,6 +1789,7 @@ namespace Emby.Server.Implementations.Data
index++;
}
+ var hasSeries = item as IHasSeries;
if (hasSeriesFields)
{
if (hasSeries != null)
@@ -1945,20 +1862,11 @@ namespace Emby.Server.Implementations.Data
index++;
}
- if (HasField(query, ItemFields.ThemeSongIds))
+ if (HasField(query, ItemFields.ExtraIds))
{
if (!reader.IsDBNull(index))
{
- item.ThemeSongIds = SplitToGuids(reader.GetString(index));
- }
- index++;
- }
-
- if (HasField(query, ItemFields.ThemeVideoIds))
- {
- if (!reader.IsDBNull(index))
- {
- item.ThemeVideoIds = SplitToGuids(reader.GetString(index));
+ item.ExtraIds = SplitToGuids(reader.GetString(index));
}
index++;
}
@@ -2055,36 +1963,14 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
- /// Gets the critic reviews.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- public List<ItemReview> GetCriticReviews(Guid itemId)
- {
- return new List<ItemReview>();
- }
-
- /// <summary>
- /// Saves the critic reviews.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="criticReviews">The critic reviews.</param>
- public void SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
- {
- }
-
- /// <summary>
/// Gets chapters for an item
/// </summary>
/// <param name="id">The id.</param>
/// <returns>IEnumerable{ChapterInfo}.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
- public List<ChapterInfo> GetChapters(Guid id)
+ public List<ChapterInfo> GetChapters(BaseItem item)
{
CheckDisposed();
- if (id == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
using (WriteLock.Read())
{
@@ -2094,11 +1980,11 @@ namespace Emby.Server.Implementations.Data
using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
{
- statement.TryBind("@ItemId", id);
+ statement.TryBind("@ItemId", item.Id);
foreach (var row in statement.ExecuteQuery())
{
- list.Add(GetChapter(row));
+ list.Add(GetChapter(row, item));
}
}
@@ -2114,13 +2000,9 @@ namespace Emby.Server.Implementations.Data
/// <param name="index">The index.</param>
/// <returns>ChapterInfo.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
- public ChapterInfo GetChapter(Guid id, int index)
+ public ChapterInfo GetChapter(BaseItem item, int index)
{
CheckDisposed();
- if (id == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
using (WriteLock.Read())
{
@@ -2128,12 +2010,12 @@ namespace Emby.Server.Implementations.Data
{
using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
{
- statement.TryBind("@ItemId", id);
+ statement.TryBind("@ItemId", item.Id);
statement.TryBind("@ChapterIndex", index);
foreach (var row in statement.ExecuteQuery())
{
- return GetChapter(row);
+ return GetChapter(row, item);
}
}
}
@@ -2146,7 +2028,7 @@ namespace Emby.Server.Implementations.Data
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>ChapterInfo.</returns>
- private ChapterInfo GetChapter(IReadOnlyList<IResultSetValue> reader)
+ private ChapterInfo GetChapter(IReadOnlyList<IResultSetValue> reader, BaseItem item)
{
var chapter = new ChapterInfo
{
@@ -2161,6 +2043,11 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(2))
{
chapter.ImagePath = reader.GetString(2);
+
+ if (!string.IsNullOrEmpty(chapter.ImagePath))
+ {
+ chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter);
+ }
}
if (!reader.IsDBNull(3))
@@ -2178,7 +2065,7 @@ namespace Emby.Server.Implementations.Data
{
CheckDisposed();
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -2188,40 +2075,72 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException("chapters");
}
- var index = 0;
-
using (WriteLock.Write())
{
using (var connection = CreateConnection())
{
connection.RunInTransaction(db =>
{
+ var idBlob = id.ToGuidBlob();
+
// First delete chapters
- db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidBlob());
+ db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
- using (var saveChapterStatement = PrepareStatement(db, "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
- {
- foreach (var chapter in chapters)
- {
- if (index > 0)
- {
- saveChapterStatement.Reset();
- }
+ InsertChapters(idBlob, chapters, db);
+
+ }, TransactionMode);
+ }
+ }
+ }
- saveChapterStatement.TryBind("@ItemId", id.ToGuidBlob());
- saveChapterStatement.TryBind("@ChapterIndex", index);
- saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
- saveChapterStatement.TryBind("@Name", chapter.Name);
- saveChapterStatement.TryBind("@ImagePath", chapter.ImagePath);
- saveChapterStatement.TryBind("@ImageDateModified", chapter.ImageDateModified);
+ private void InsertChapters(byte[] idBlob, List<ChapterInfo> chapters, IDatabaseConnection db)
+ {
+ var startIndex = 0;
+ var limit = 100;
+ var chapterIndex = 0;
- saveChapterStatement.MoveNext();
+ while (startIndex < chapters.Count)
+ {
+ var insertText = new StringBuilder("insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values ");
- index++;
- }
- }
- }, TransactionMode);
+ var endIndex = Math.Min(chapters.Count, startIndex + limit);
+ var isSubsequentRow = false;
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ if (isSubsequentRow)
+ {
+ insertText.Append(",");
+ }
+
+ insertText.AppendFormat("(@ItemId, @ChapterIndex{0}, @StartPositionTicks{0}, @Name{0}, @ImagePath{0}, @ImageDateModified{0})", i.ToString(CultureInfo.InvariantCulture));
+ isSubsequentRow = true;
}
+
+ using (var statement = PrepareStatementSafe(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", idBlob);
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var chapter = chapters[i];
+
+ statement.TryBind("@ChapterIndex" + index, chapterIndex);
+ statement.TryBind("@StartPositionTicks" + index, chapter.StartPositionTicks);
+ statement.TryBind("@Name" + index, chapter.Name);
+ statement.TryBind("@ImagePath" + index, chapter.ImagePath);
+ statement.TryBind("@ImageDateModified" + index, chapter.ImageDateModified);
+
+ chapterIndex++;
+ }
+
+ statement.Reset();
+ statement.MoveNext();
+ }
+
+ startIndex += limit;
}
}
@@ -2232,11 +2151,6 @@ namespace Emby.Server.Implementations.Data
return false;
}
- if (query.SimilarTo != null && query.User != null)
- {
- //return true;
- }
-
var sortingFields = query.OrderBy.Select(i => i.Item1).ToList();
if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
@@ -2296,7 +2210,7 @@ namespace Emby.Server.Implementations.Data
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
.ToList();
- private IEnumerable<string> GetColumnNamesFromField(ItemFields field)
+ private string[] GetColumnNamesFromField(ItemFields field)
{
if (field == ItemFields.Settings)
{
@@ -2318,17 +2232,20 @@ namespace Emby.Server.Implementations.Data
{
return new[] { "Tags" };
}
+ if (field == ItemFields.IsHD)
+ {
+ return Array.Empty<string>();
+ }
return new[] { field.ToString() };
}
private bool HasField(InternalItemsQuery query, ItemFields name)
{
- var fields = query.DtoOptions.Fields;
-
switch (name)
{
- case ItemFields.HomePageUrl:
+ case ItemFields.Tags:
+ return query.DtoOptions.ContainsField(name) || HasProgramAttributes(query);
case ItemFields.CustomRating:
case ItemFields.ProductionLocations:
case ItemFields.Settings:
@@ -2336,23 +2253,20 @@ namespace Emby.Server.Implementations.Data
case ItemFields.Taglines:
case ItemFields.SortName:
case ItemFields.Studios:
- case ItemFields.Tags:
- case ItemFields.ThemeSongIds:
- case ItemFields.ThemeVideoIds:
+ case ItemFields.ExtraIds:
case ItemFields.DateCreated:
case ItemFields.Overview:
case ItemFields.Genres:
case ItemFields.DateLastMediaAdded:
- case ItemFields.ExternalEtag:
case ItemFields.PresentationUniqueKey:
case ItemFields.InheritedParentalRatingValue:
case ItemFields.ExternalSeriesId:
case ItemFields.SeriesPresentationUniqueKey:
case ItemFields.DateLastRefreshed:
case ItemFields.DateLastSaved:
- return fields.Contains(name);
+ return query.DtoOptions.ContainsField(name);
case ItemFields.ServiceName:
- return true;
+ return HasServiceName(query);
default:
return true;
}
@@ -2382,10 +2296,7 @@ namespace Emby.Server.Implementations.Data
var types = new string[]
{
"Program",
- "Recording",
"TvChannel",
- "LiveTvAudioRecording",
- "LiveTvVideoRecording",
"LiveTvProgram",
"LiveTvTvChannel"
};
@@ -2393,6 +2304,36 @@ namespace Emby.Server.Implementations.Data
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
}
+ private bool HasServiceName(InternalItemsQuery query)
+ {
+ var excludeParentTypes = new string[]
+ {
+ "Series",
+ "Season",
+ "MusicAlbum",
+ "MusicArtist",
+ "PhotoAlbum"
+ };
+
+ if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (query.IncludeItemTypes.Length == 0)
+ {
+ return true;
+ }
+
+ var types = new string[]
+ {
+ "TvChannel",
+ "LiveTvTvChannel"
+ };
+
+ return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ }
+
private bool HasStartDate(InternalItemsQuery query)
{
var excludeParentTypes = new string[]
@@ -2417,9 +2358,6 @@ namespace Emby.Server.Implementations.Data
var types = new string[]
{
"Program",
- "Recording",
- "LiveTvAudioRecording",
- "LiveTvVideoRecording",
"LiveTvProgram"
};
@@ -2481,9 +2419,7 @@ namespace Emby.Server.Implementations.Data
"MusicAlbum",
"MusicVideo",
"AudioBook",
- "AudioPodcast",
- "LiveTvAudioRecording",
- "Recording"
+ "AudioPodcast"
};
return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
@@ -2534,13 +2470,8 @@ namespace Emby.Server.Implementations.Data
if (!HasProgramAttributes(query))
{
- list.Remove("IsKids");
list.Remove("IsMovie");
- list.Remove("IsSports");
list.Remove("IsSeries");
- list.Remove("IsLive");
- list.Remove("IsNews");
- list.Remove("IsPremiere");
list.Remove("EpisodeTitle");
list.Remove("IsRepeat");
list.Remove("ShowId");
@@ -2571,7 +2502,6 @@ namespace Emby.Server.Implementations.Data
if (!HasSeriesFields(query))
{
list.Remove("SeriesId");
- list.Remove("SeriesName");
}
if (!HasEpisodeAttributes(query))
@@ -2587,13 +2517,13 @@ namespace Emby.Server.Implementations.Data
if (EnableJoinUserData(query))
{
- list.Add("UserData.UserId");
- list.Add("UserData.lastPlayedDate");
- list.Add("UserData.playbackPositionTicks");
- list.Add("UserData.playcount");
- list.Add("UserData.isFavorite");
- list.Add("UserData.played");
- list.Add("UserData.rating");
+ list.Add("UserDatas.UserId");
+ list.Add("UserDatas.lastPlayedDate");
+ list.Add("UserDatas.playbackPositionTicks");
+ list.Add("UserDatas.playcount");
+ list.Add("UserDatas.isFavorite");
+ list.Add("UserDatas.played");
+ list.Add("UserDatas.rating");
}
if (query.SimilarTo != null)
@@ -2603,14 +2533,24 @@ namespace Emby.Server.Implementations.Data
var builder = new StringBuilder();
builder.Append("(");
- builder.Append("((OfficialRating=@ItemOfficialRating) * 10)");
- //builder.Append("+ ((ProductionYear=@ItemProductionYear) * 10)");
+ if (string.IsNullOrEmpty(item.OfficialRating))
+ {
+ builder.Append("((OfficialRating is null) * 10)");
+ }
+ else
+ {
+ builder.Append("((OfficialRating=@ItemOfficialRating) * 10)");
+ }
- builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 2 Else 0 End )");
- builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 5 Then 2 Else 0 End )");
+ if (item.ProductionYear.HasValue)
+ {
+ //builder.Append("+ ((ProductionYear=@ItemProductionYear) * 10)");
+ builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 10 Else 0 End )");
+ builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 5 Then 5 Else 0 End )");
+ }
//// genres, tags
- builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and Type in (2,3,4,5) and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId and Type in (2,3,4,5))) * 10)");
+ builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId)) * 10)");
//builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and Type=3 and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId and type=3)) * 3)");
@@ -2623,24 +2563,56 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
var excludeIds = query.ExcludeItemIds.ToList();
- excludeIds.Add(item.Id.ToString("N"));
+ excludeIds.Add(item.Id);
+ excludeIds.AddRange(item.ExtraIds);
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+ query.ExcludeItemIds = excludeIds.ToArray(excludeIds.Count);
+ query.ExcludeProviderIds = item.ProviderIds;
+ }
+
+ if (!string.IsNullOrEmpty(query.SearchTerm))
+ {
+ var builder = new StringBuilder();
+ builder.Append("(");
+
+ builder.Append("((CleanName like @SearchTermStartsWith or (OriginalTitle not null and OriginalTitle like @SearchTermStartsWith)) * 10)");
+
+ if (query.SearchTerm.Length > 1)
{
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- excludeIds.AddRange(hasTrailers.GetTrailerIds().Select(i => i.ToString("N")));
- }
+ builder.Append("+ ((CleanName like @SearchTermContains or (OriginalTitle not null and OriginalTitle like @SearchTermContains)) * 10)");
}
- query.ExcludeItemIds = excludeIds.ToArray(excludeIds.Count);
- query.ExcludeProviderIds = item.ProviderIds;
+ builder.Append(") as SearchScore");
+
+ list.Add(builder.ToString());
}
return list.ToArray(list.Count);
}
+ private void BindSearchParams(InternalItemsQuery query, IStatement statement)
+ {
+ var searchTerm = query.SearchTerm;
+
+ if (string.IsNullOrEmpty(searchTerm))
+ {
+ return;
+ }
+
+ searchTerm = FixUnicodeChars(searchTerm);
+ searchTerm = GetCleanValue(searchTerm);
+
+ var commandText = statement.SQL;
+ if (commandText.IndexOf("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
+ }
+ if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ statement.TryBind("@SearchTermContains", "%" + searchTerm + "%");
+ }
+ }
+
private void BindSimilarParams(InternalItemsQuery query, IStatement statement)
{
var item = query.SimilarTo;
@@ -2650,9 +2622,22 @@ namespace Emby.Server.Implementations.Data
return;
}
- statement.TryBind("@ItemOfficialRating", item.OfficialRating);
- statement.TryBind("@ItemProductionYear", item.ProductionYear ?? 0);
- statement.TryBind("@SimilarItemId", item.Id);
+ var commandText = statement.SQL;
+
+ if (commandText.IndexOf("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ statement.TryBind("@ItemOfficialRating", item.OfficialRating);
+ }
+
+ if (commandText.IndexOf("@ItemProductionYear", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ statement.TryBind("@ItemProductionYear", item.ProductionYear ?? 0);
+ }
+
+ if (commandText.IndexOf("@SimilarItemId", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ statement.TryBind("@SimilarItemId", item.Id);
+ }
}
private string GetJoinUserDataText(InternalItemsQuery query)
@@ -2662,7 +2647,7 @@ namespace Emby.Server.Implementations.Data
return string.Empty;
}
- return " left join UserData on UserDataKey=UserData.Key And (UserId=@UserId)";
+ return " left join UserDatas on UserDataKey=UserDatas.Key And (UserId=@UserId)";
}
private string GetGroupBy(InternalItemsQuery query)
@@ -2732,10 +2717,11 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
@@ -2808,15 +2794,17 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
var hasEpisodeAttributes = HasEpisodeAttributes(query);
+ var hasServiceName = HasServiceName(query);
var hasProgramAttributes = HasProgramAttributes(query);
var hasStartDate = HasStartDate(query);
var hasTrailerTypes = HasTrailerTypes(query);
@@ -2825,7 +2813,7 @@ namespace Emby.Server.Implementations.Data
foreach (var row in statement.ExecuteQuery())
{
- var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
+ var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
if (item != null)
{
list.Add(item);
@@ -2860,6 +2848,28 @@ namespace Emby.Server.Implementations.Data
}
}
+ private string FixUnicodeChars(string buffer)
+ {
+ if (buffer.IndexOf('\u2013') > -1) buffer = buffer.Replace('\u2013', '-'); // en dash
+ if (buffer.IndexOf('\u2014') > -1) buffer = buffer.Replace('\u2014', '-'); // em dash
+ if (buffer.IndexOf('\u2015') > -1) buffer = buffer.Replace('\u2015', '-'); // horizontal bar
+ if (buffer.IndexOf('\u2017') > -1) buffer = buffer.Replace('\u2017', '_'); // double low line
+ if (buffer.IndexOf('\u2018') > -1) buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
+ if (buffer.IndexOf('\u2019') > -1) buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
+ if (buffer.IndexOf('\u201a') > -1) buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
+ if (buffer.IndexOf('\u201b') > -1) buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
+ if (buffer.IndexOf('\u201c') > -1) buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
+ if (buffer.IndexOf('\u201d') > -1) buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
+ if (buffer.IndexOf('\u201e') > -1) buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
+ if (buffer.IndexOf('\u2026') > -1) buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
+ if (buffer.IndexOf('\u2032') > -1) buffer = buffer.Replace('\u2032', '\''); // prime
+ if (buffer.IndexOf('\u2033') > -1) buffer = buffer.Replace('\u2033', '\"'); // double prime
+ if (buffer.IndexOf('\u0060') > -1) buffer = buffer.Replace('\u0060', '\''); // grave accent
+ if (buffer.IndexOf('\u00B4') > -1) buffer = buffer.Replace('\u00B4', '\''); // acute accent
+
+ return buffer;
+ }
+
private void AddItem(List<BaseItem> items, BaseItem newItem)
{
var providerIds = newItem.ProviderIds.ToList();
@@ -2989,15 +2999,15 @@ namespace Emby.Server.Implementations.Data
if (EnableGroupByPresentationUniqueKey(query))
{
- commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
}
else if (query.GroupBySeriesPresentationUniqueKey)
{
- commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
}
else
{
- commandText += " select count (guid)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
}
commandText += GetJoinUserDataText(query);
@@ -3020,15 +3030,17 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
var hasEpisodeAttributes = HasEpisodeAttributes(query);
+ var hasServiceName = HasServiceName(query);
var hasProgramAttributes = HasProgramAttributes(query);
var hasStartDate = HasStartDate(query);
var hasTrailerTypes = HasTrailerTypes(query);
@@ -3037,7 +3049,7 @@ namespace Emby.Server.Implementations.Data
foreach (var row in statement.ExecuteQuery())
{
- var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
+ var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
if (item != null)
{
list.Add(item);
@@ -3052,10 +3064,11 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
@@ -3083,12 +3096,19 @@ namespace Emby.Server.Implementations.Data
{
if (orderBy.Count == 0)
{
- orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
- orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
+ orderBy.Add(new ValueTuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
+ orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
//orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
}
}
+ if (!string.IsNullOrEmpty(query.SearchTerm))
+ {
+ orderBy = new List<(string, SortOrder)>();
+ orderBy.Add(new ValueTuple<string, SortOrder>("SearchScore", SortOrder.Descending));
+ orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
+ }
+
query.OrderBy = orderBy.ToArray();
if (orderBy.Count == 0)
@@ -3111,81 +3131,81 @@ namespace Emby.Server.Implementations.Data
}).ToArray());
}
- private Tuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
+ private ValueTuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
// TODO
- return new Tuple<string, bool>("SortName", false);
+ return new ValueTuple<string, bool>("SortName", false);
}
if (string.Equals(name, ItemSortBy.Runtime, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("RuntimeTicks", false);
+ return new ValueTuple<string, bool>("RuntimeTicks", false);
}
if (string.Equals(name, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("RANDOM()", false);
+ return new ValueTuple<string, bool>("RANDOM()", false);
}
if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase))
{
if (query.GroupBySeriesPresentationUniqueKey)
{
- return new Tuple<string, bool>("MAX(LastPlayedDate)", false);
+ return new ValueTuple<string, bool>("MAX(LastPlayedDate)", false);
}
- return new Tuple<string, bool>("LastPlayedDate", false);
+ return new ValueTuple<string, bool>("LastPlayedDate", false);
}
if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("PlayCount", false);
+ return new ValueTuple<string, bool>("PlayCount", false);
}
if (string.Equals(name, ItemSortBy.IsFavoriteOrLiked, StringComparison.OrdinalIgnoreCase))
{
// (Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 2 Else 0 End )
- return new Tuple<string, bool>("(Select Case When IsFavorite is null Then 0 Else IsFavorite End )", true);
+ return new ValueTuple<string, bool>("(Select Case When IsFavorite is null Then 0 Else IsFavorite End )", true);
}
if (string.Equals(name, ItemSortBy.IsFolder, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("IsFolder", true);
+ return new ValueTuple<string, bool>("IsFolder", true);
}
if (string.Equals(name, ItemSortBy.IsPlayed, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("played", true);
+ return new ValueTuple<string, bool>("played", true);
}
if (string.Equals(name, ItemSortBy.IsUnplayed, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("played", false);
+ return new ValueTuple<string, bool>("played", false);
}
if (string.Equals(name, ItemSortBy.DateLastContentAdded, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("DateLastMediaAdded", false);
+ return new ValueTuple<string, bool>("DateLastMediaAdded", false);
}
if (string.Equals(name, ItemSortBy.Artist, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=0 LIMIT 1)", false);
+ return new ValueTuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=0 LIMIT 1)", false);
}
if (string.Equals(name, ItemSortBy.AlbumArtist, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=1 LIMIT 1)", false);
+ return new ValueTuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=1 LIMIT 1)", false);
}
if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("InheritedParentalRatingValue", false);
+ return new ValueTuple<string, bool>("InheritedParentalRatingValue", false);
}
if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=3 LIMIT 1)", false);
+ return new ValueTuple<string, bool>("(select CleanValue from itemvalues where ItemId=Guid and Type=3 LIMIT 1)", false);
}
if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", false);
+ return new ValueTuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", false);
}
if (string.Equals(name, ItemSortBy.SeriesSortName, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("SeriesName", false);
+ return new ValueTuple<string, bool>("SeriesName", false);
}
- return new Tuple<string, bool>(name, false);
+ return new ValueTuple<string, bool>(name, false);
}
public List<Guid> GetItemIdsList(InternalItemsQuery query)
@@ -3240,10 +3260,11 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
@@ -3311,7 +3332,7 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
// Running this again will bind the params
@@ -3405,15 +3426,15 @@ namespace Emby.Server.Implementations.Data
if (EnableGroupByPresentationUniqueKey(query))
{
- commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
}
else if (query.GroupBySeriesPresentationUniqueKey)
{
- commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
}
else
{
- commandText += " select count (guid)" + GetFromText();
+ commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
}
commandText += GetJoinUserDataText(query);
@@ -3437,10 +3458,11 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
@@ -3458,10 +3480,11 @@ namespace Emby.Server.Implementations.Data
{
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
// Running this again will bind the params
GetWhereClauses(query, statement);
@@ -3527,14 +3550,69 @@ namespace Emby.Server.Implementations.Data
{
//whereClauses.Add("(UserId is null or UserId=@UserId)");
}
+
+ var minWidth = query.MinWidth;
+ var maxWidth = query.MaxWidth;
+
if (query.IsHD.HasValue)
{
- whereClauses.Add("IsHD=@IsHD");
+ var threshold = 1200;
+ if (query.IsHD.Value)
+ {
+ minWidth = threshold;
+ }
+ else
+ {
+ maxWidth = threshold - 1;
+ }
+ }
+
+ if (query.Is4K.HasValue)
+ {
+ var threshold = 3800;
+ if (query.Is4K.Value)
+ {
+ minWidth = threshold;
+ }
+ else
+ {
+ maxWidth = threshold - 1;
+ }
+ }
+
+ if (minWidth.HasValue)
+ {
+ whereClauses.Add("Width>=@MinWidth");
+ if (statement != null)
+ {
+ statement.TryBind("@MinWidth", minWidth);
+ }
+ }
+ if (query.MinHeight.HasValue)
+ {
+ whereClauses.Add("Height>=@MinHeight");
if (statement != null)
{
- statement.TryBind("@IsHD", query.IsHD);
+ statement.TryBind("@MinHeight", query.MinHeight);
}
}
+ if (maxWidth.HasValue)
+ {
+ whereClauses.Add("Width<=@MaxWidth");
+ if (statement != null)
+ {
+ statement.TryBind("@MaxWidth", maxWidth);
+ }
+ }
+ if (query.MaxHeight.HasValue)
+ {
+ whereClauses.Add("Height<=@MaxHeight");
+ if (statement != null)
+ {
+ statement.TryBind("@MaxHeight", query.MaxHeight);
+ }
+ }
+
if (query.IsLocked.HasValue)
{
whereClauses.Add("IsLocked=@IsLocked");
@@ -3544,140 +3622,127 @@ namespace Emby.Server.Implementations.Data
}
}
- var exclusiveProgramAttribtues = !(query.IsMovie ?? true) ||
- !(query.IsSports ?? true) ||
- !(query.IsKids ?? true) ||
- !(query.IsNews ?? true) ||
- !(query.IsSeries ?? true);
+ var tags = query.Tags.ToList();
+ var excludeTags = query.ExcludeTags.ToList();
+
+ //if (!(query.IsMovie ?? true) || !(query.IsSeries ?? true))
+ //{
+ // if (query.IsMovie.HasValue)
+ // {
+ // var alternateTypes = new List<string>();
+ // if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+ // {
+ // alternateTypes.Add(typeof(Movie).FullName);
+ // }
+ // if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+ // {
+ // alternateTypes.Add(typeof(Trailer).FullName);
+ // }
+
+ // if (alternateTypes.Count == 0)
+ // {
+ // whereClauses.Add("IsMovie=@IsMovie");
+ // if (statement != null)
+ // {
+ // statement.TryBind("@IsMovie", query.IsMovie);
+ // }
+ // }
+ // else
+ // {
+ // whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
+ // if (statement != null)
+ // {
+ // statement.TryBind("@IsMovie", query.IsMovie);
+ // }
+ // }
+ // }
+ //}
+ //else
+ //{
- if (exclusiveProgramAttribtues)
+ //}
+
+ if (query.IsMovie ?? false)
{
- if (query.IsMovie.HasValue)
- {
- var alternateTypes = new List<string>();
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
- {
- alternateTypes.Add(typeof(Movie).FullName);
- }
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
- {
- alternateTypes.Add(typeof(Trailer).FullName);
- }
+ var programAttribtues = new List<string>();
- if (alternateTypes.Count == 0)
- {
- whereClauses.Add("IsMovie=@IsMovie");
- if (statement != null)
- {
- statement.TryBind("@IsMovie", query.IsMovie);
- }
- }
- else
- {
- whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
- if (statement != null)
- {
- statement.TryBind("@IsMovie", query.IsMovie);
- }
- }
+ var alternateTypes = new List<string>();
+ if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+ {
+ alternateTypes.Add(typeof(Movie).FullName);
}
- if (query.IsSeries.HasValue)
+ if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
{
- whereClauses.Add("IsSeries=@IsSeries");
- if (statement != null)
- {
- statement.TryBind("@IsSeries", query.IsSeries);
- }
+ alternateTypes.Add(typeof(Trailer).FullName);
}
- if (query.IsNews.HasValue)
+
+ if (alternateTypes.Count == 0)
{
- whereClauses.Add("IsNews=@IsNews");
- if (statement != null)
- {
- statement.TryBind("@IsNews", query.IsNews);
- }
+ programAttribtues.Add("IsMovie=@IsMovie");
}
- if (query.IsKids.HasValue)
+ else
{
- whereClauses.Add("IsKids=@IsKids");
- if (statement != null)
- {
- statement.TryBind("@IsKids", query.IsKids);
- }
+ programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)");
}
- if (query.IsSports.HasValue)
+
+ if (statement != null)
{
- whereClauses.Add("IsSports=@IsSports");
- if (statement != null)
- {
- statement.TryBind("@IsSports", query.IsSports);
- }
+ statement.TryBind("@IsMovie", true);
}
+
+ whereClauses.Add("(" + string.Join(" OR ", programAttribtues.ToArray(programAttribtues.Count)) + ")");
}
- else
+ else if (query.IsMovie.HasValue)
{
- var programAttribtues = new List<string>();
- if (query.IsMovie ?? false)
+ whereClauses.Add("IsMovie=@IsMovie");
+ if (statement != null)
{
- var alternateTypes = new List<string>();
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
- {
- alternateTypes.Add(typeof(Movie).FullName);
- }
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
- {
- alternateTypes.Add(typeof(Trailer).FullName);
- }
+ statement.TryBind("@IsMovie", query.IsMovie);
+ }
+ }
- if (alternateTypes.Count == 0)
- {
- programAttribtues.Add("IsMovie=@IsMovie");
- }
- else
- {
- programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)");
- }
+ if (query.IsSeries.HasValue)
+ {
+ whereClauses.Add("IsSeries=@IsSeries");
+ if (statement != null)
+ {
+ statement.TryBind("@IsSeries", query.IsSeries);
+ }
+ }
- if (statement != null)
- {
- statement.TryBind("@IsMovie", true);
- }
+ if (query.IsSports.HasValue)
+ {
+ if (query.IsSports.Value)
+ {
+ tags.Add("Sports");
}
- if (query.IsSports ?? false)
+ else
{
- programAttribtues.Add("IsSports=@IsSports");
- if (statement != null)
- {
- statement.TryBind("@IsSports", query.IsSports);
- }
+ excludeTags.Add("Sports");
}
- if (query.IsNews ?? false)
+ }
+
+ if (query.IsNews.HasValue)
+ {
+ if (query.IsNews.Value)
{
- programAttribtues.Add("IsNews=@IsNews");
- if (statement != null)
- {
- statement.TryBind("@IsNews", query.IsNews);
- }
+ tags.Add("News");
}
- if (query.IsSeries ?? false)
+ else
{
- programAttribtues.Add("IsSeries=@IsSeries");
- if (statement != null)
- {
- statement.TryBind("@IsSeries", query.IsSeries);
- }
+ excludeTags.Add("News");
}
- if (query.IsKids ?? false)
+ }
+
+ if (query.IsKids.HasValue)
+ {
+ if (query.IsKids.Value)
{
- programAttribtues.Add("IsKids=@IsKids");
- if (statement != null)
- {
- statement.TryBind("@IsKids", query.IsKids);
- }
+ tags.Add("Kids");
}
- if (programAttribtues.Count > 0)
+ else
{
- whereClauses.Add("(" + string.Join(" OR ", programAttribtues.ToArray(programAttribtues.Count)) + ")");
+ excludeTags.Add("Kids");
}
}
@@ -3686,6 +3751,11 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("SimilarityScore > " + (query.MinSimilarityScore - 1).ToString(CultureInfo.InvariantCulture));
}
+ if (!string.IsNullOrEmpty(query.SearchTerm))
+ {
+ whereClauses.Add("SearchScore > 0");
+ }
+
if (query.IsFolder.HasValue)
{
whereClauses.Add("IsFolder=@IsFolder");
@@ -3710,50 +3780,55 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(string.Format("type in ({0})", inClause));
}
- var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
- if (excludeTypes.Length == 1)
+ // Only specify excluded types if no included types are specified
+ if (includeTypes.Length == 0)
{
- whereClauses.Add("type<>@type");
- if (statement != null)
+ var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
+ if (excludeTypes.Length == 1)
+ {
+ whereClauses.Add("type<>@type");
+ if (statement != null)
+ {
+ statement.TryBind("@type", excludeTypes[0]);
+ }
+ }
+ else if (excludeTypes.Length > 1)
{
- statement.TryBind("@type", excludeTypes[0]);
+ var inClause = string.Join(",", excludeTypes.Select(i => "'" + i + "'").ToArray());
+ whereClauses.Add(string.Format("type not in ({0})", inClause));
}
}
- else if (excludeTypes.Length > 1)
- {
- var inClause = string.Join(",", excludeTypes.Select(i => "'" + i + "'").ToArray());
- whereClauses.Add(string.Format("type not in ({0})", inClause));
- }
if (query.ChannelIds.Length == 1)
{
whereClauses.Add("ChannelId=@ChannelId");
if (statement != null)
{
- statement.TryBind("@ChannelId", query.ChannelIds[0]);
+ statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N"));
}
}
else if (query.ChannelIds.Length > 1)
{
- var inClause = string.Join(",", query.ChannelIds.Where(IsValidId).Select(i => "'" + i + "'").ToArray());
+ var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N") + "'").ToArray());
whereClauses.Add(string.Format("ChannelId in ({0})", inClause));
}
- if (query.ParentId.HasValue)
+ if (!query.ParentId.Equals(Guid.Empty))
{
whereClauses.Add("ParentId=@ParentId");
if (statement != null)
{
- statement.TryBind("@ParentId", query.ParentId.Value);
+ statement.TryBind("@ParentId", query.ParentId);
}
}
if (!string.IsNullOrWhiteSpace(query.Path))
{
+ //whereClauses.Add("(Path=@Path COLLATE NOCASE)");
whereClauses.Add("Path=@Path");
if (statement != null)
{
- statement.TryBind("@Path", query.Path);
+ statement.TryBind("@Path", GetPathToSave(query.Path));
}
}
@@ -3847,21 +3922,37 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value);
}
}
- if (query.MinEndDate.HasValue)
+
+ var minEndDate = query.MinEndDate;
+ var maxEndDate = query.MaxEndDate;
+
+ if (query.HasAired.HasValue)
+ {
+ if (query.HasAired.Value)
+ {
+ maxEndDate = DateTime.UtcNow;
+ }
+ else
+ {
+ minEndDate = DateTime.UtcNow;
+ }
+ }
+
+ if (minEndDate.HasValue)
{
whereClauses.Add("EndDate>=@MinEndDate");
if (statement != null)
{
- statement.TryBind("@MinEndDate", query.MinEndDate.Value);
+ statement.TryBind("@MinEndDate", minEndDate.Value);
}
}
- if (query.MaxEndDate.HasValue)
+ if (maxEndDate.HasValue)
{
whereClauses.Add("EndDate<=@MaxEndDate");
if (statement != null)
{
- statement.TryBind("@MaxEndDate", query.MaxEndDate.Value);
+ statement.TryBind("@MaxEndDate", maxEndDate.Value);
}
}
@@ -3955,7 +4046,8 @@ namespace Emby.Server.Implementations.Data
{
var paramName = "@PersonId" + index;
- clauses.Add("(select Name from TypedBaseItems where guid=" + paramName + ") in (select Name from People where ItemId=Guid)");
+ clauses.Add("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=" + paramName + ")))");
+
if (statement != null)
{
statement.TryBind(paramName, personId.ToGuidBlob());
@@ -4012,14 +4104,19 @@ namespace Emby.Server.Implementations.Data
}
}
- if (!string.IsNullOrWhiteSpace(query.NameContains))
+ // These are the same, for now
+ var nameContains = query.NameContains;
+ if (!string.IsNullOrWhiteSpace(nameContains))
{
- whereClauses.Add("CleanName like @NameContains");
+ whereClauses.Add("(CleanName like @NameContains or OriginalTitle like @NameContains)");
if (statement != null)
{
- statement.TryBind("@NameContains", "%" + GetCleanValue(query.NameContains) + "%");
+ nameContains = FixUnicodeChars(nameContains);
+
+ statement.TryBind("@NameContains", "%" + GetCleanValue(nameContains) + "%");
}
}
+
if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
{
whereClauses.Add("SortName like @NameStartsWith");
@@ -4111,17 +4208,32 @@ namespace Emby.Server.Implementations.Data
{
if (query.IsPlayed.HasValue)
{
- if (query.IsPlayed.Value)
+ // We should probably figure this out for all folders, but for right now, this is the only place where we need it
+ if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], typeof(Series).Name, StringComparison.OrdinalIgnoreCase))
{
- whereClauses.Add("(played=@IsPlayed)");
+ if (query.IsPlayed.Value)
+ {
+ whereClauses.Add("PresentationUniqueKey not in (select S.SeriesPresentationUniqueKey from TypedBaseitems S left join UserDatas UD on S.UserDataKey=UD.Key And UD.UserId=@UserId where Coalesce(UD.Played, 0)=0 and S.IsFolder=0 and S.IsVirtualItem=0 and S.SeriesPresentationUniqueKey not null)");
+ }
+ else
+ {
+ whereClauses.Add("PresentationUniqueKey in (select S.SeriesPresentationUniqueKey from TypedBaseitems S left join UserDatas UD on S.UserDataKey=UD.Key And UD.UserId=@UserId where Coalesce(UD.Played, 0)=0 and S.IsFolder=0 and S.IsVirtualItem=0 and S.SeriesPresentationUniqueKey not null)");
+ }
}
else
{
- whereClauses.Add("(played is null or played=@IsPlayed)");
- }
- if (statement != null)
- {
- statement.TryBind("@IsPlayed", query.IsPlayed.Value);
+ if (query.IsPlayed.Value)
+ {
+ whereClauses.Add("(played=@IsPlayed)");
+ }
+ else
+ {
+ whereClauses.Add("(played is null or played=@IsPlayed)");
+ }
+ if (statement != null)
+ {
+ statement.TryBind("@IsPlayed", query.IsPlayed.Value);
+ }
}
}
}
@@ -4146,7 +4258,45 @@ namespace Emby.Server.Implementations.Data
{
var paramName = "@ArtistIds" + index;
- clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
+ clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, artistId.ToGuidBlob());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
+ }
+
+ if (query.AlbumArtistIds.Length > 0)
+ {
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var artistId in query.AlbumArtistIds)
+ {
+ var paramName = "@ArtistIds" + index;
+
+ clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, artistId.ToGuidBlob());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
+ }
+
+ if (query.ContributingArtistIds.Length > 0)
+ {
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var artistId in query.ContributingArtistIds)
+ {
+ var paramName = "@ArtistIds" + index;
+
+ clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type=1))");
if (statement != null)
{
statement.TryBind(paramName, artistId.ToGuidBlob());
@@ -4184,7 +4334,7 @@ namespace Emby.Server.Implementations.Data
{
var paramName = "@ExcludeArtistId" + index;
- clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
+ clauses.Add("(guid not in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
if (statement != null)
{
statement.TryBind(paramName, artistId.ToGuidBlob());
@@ -4203,7 +4353,7 @@ namespace Emby.Server.Implementations.Data
{
var paramName = "@GenreId" + index;
- clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
+ clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))");
if (statement != null)
{
statement.TryBind(paramName, genreId.ToGuidBlob());
@@ -4231,11 +4381,11 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(clause);
}
- if (query.Tags.Length > 0)
+ if (tags.Count > 0)
{
var clauses = new List<string>();
var index = 0;
- foreach (var item in query.Tags)
+ foreach (var item in tags)
{
clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
if (statement != null)
@@ -4248,6 +4398,23 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(clause);
}
+ if (excludeTags.Count > 0)
+ {
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var item in excludeTags)
+ {
+ clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
+ if (statement != null)
+ {
+ statement.TryBind("@ExcludeTag" + index, GetCleanValue(item));
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
+ }
+
if (query.StudioIds.Length > 0)
{
var clauses = new List<string>();
@@ -4256,7 +4423,8 @@ namespace Emby.Server.Implementations.Data
{
var paramName = "@StudioId" + index;
- clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
+ clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))");
+
if (statement != null)
{
statement.TryBind(paramName, studioId.ToGuidBlob());
@@ -4314,6 +4482,18 @@ namespace Emby.Server.Implementations.Data
}
}
+ if (query.HasOfficialRating.HasValue)
+ {
+ if (query.HasOfficialRating.Value)
+ {
+ whereClauses.Add("(OfficialRating not null AND OfficialRating<>'')");
+ }
+ else
+ {
+ whereClauses.Add("(OfficialRating is null OR OfficialRating='')");
+ }
+ }
+
if (query.HasOverview.HasValue)
{
if (query.HasOverview.Value)
@@ -4326,6 +4506,18 @@ namespace Emby.Server.Implementations.Data
}
}
+ if (query.HasOwnerId.HasValue)
+ {
+ if (query.HasOwnerId.Value)
+ {
+ whereClauses.Add("OwnerId not null");
+ }
+ else
+ {
+ whereClauses.Add("OwnerId is null");
+ }
+ }
+
if (!string.IsNullOrWhiteSpace(query.HasNoAudioTrackWithLanguage))
{
whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Audio' and MediaStreams.Language=@HasNoAudioTrackWithLanguage limit 1) is null)");
@@ -4362,6 +4554,18 @@ namespace Emby.Server.Implementations.Data
}
}
+ if (query.HasSubtitles.HasValue)
+ {
+ if (query.HasSubtitles.Value)
+ {
+ whereClauses.Add("((select type from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' limit 1) not null)");
+ }
+ else
+ {
+ whereClauses.Add("((select type from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' limit 1) is null)");
+ }
+ }
+
if (query.HasChapterImages.HasValue)
{
if (query.HasChapterImages.Value)
@@ -4379,6 +4583,21 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)");
}
+ if (query.IsDeadArtist.HasValue && query.IsDeadArtist.Value)
+ {
+ whereClauses.Add("CleanName not in (Select CleanValue From ItemValues where Type in (0,1))");
+ }
+
+ if (query.IsDeadStudio.HasValue && query.IsDeadStudio.Value)
+ {
+ whereClauses.Add("CleanName not in (Select CleanValue From ItemValues where Type = 3)");
+ }
+
+ if (query.IsDeadPerson.HasValue && query.IsDeadPerson.Value)
+ {
+ whereClauses.Add("Name not in (Select Name From People)");
+ }
+
if (query.Years.Length == 1)
{
whereClauses.Add("ProductionYear=@Years");
@@ -4450,7 +4669,7 @@ namespace Emby.Server.Implementations.Data
includeIds.Add("Guid = @IncludeId" + index);
if (statement != null)
{
- statement.TryBind("@IncludeId" + index, new Guid(id));
+ statement.TryBind("@IncludeId" + index, id);
}
index++;
}
@@ -4467,7 +4686,7 @@ namespace Emby.Server.Implementations.Data
excludeIds.Add("Guid <> @ExcludeId" + index);
if (statement != null)
{
- statement.TryBind("@ExcludeId" + index, new Guid(id));
+ statement.TryBind("@ExcludeId" + index, id);
}
index++;
}
@@ -4499,7 +4718,40 @@ namespace Emby.Server.Implementations.Data
break;
}
- whereClauses.Add(string.Join(" AND ", excludeIds.ToArray()));
+ if (excludeIds.Count > 0)
+ {
+ whereClauses.Add(string.Join(" AND ", excludeIds.ToArray()));
+ }
+ }
+
+ if (query.HasAnyProviderId.Count > 0)
+ {
+ var hasProviderIds = new List<string>();
+
+ var index = 0;
+ foreach (var pair in query.HasAnyProviderId)
+ {
+ if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var paramName = "@HasAnyProviderId" + index;
+ //hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
+ hasProviderIds.Add("ProviderIds like " + paramName + "");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
+ }
+ index++;
+
+ break;
+ }
+
+ if (hasProviderIds.Count > 0)
+ {
+ whereClauses.Add("(" + string.Join(" OR ", hasProviderIds.ToArray()) + ")");
+ }
}
if (query.HasImdbId.HasValue)
@@ -4516,33 +4768,11 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("ProviderIds like '%tvdb=%'");
}
- if (query.HasThemeSong.HasValue)
- {
- if (query.HasThemeSong.Value)
- {
- whereClauses.Add("ThemeSongIds not null");
- }
- else
- {
- whereClauses.Add("ThemeSongIds is null");
- }
- }
- if (query.HasThemeVideo.HasValue)
- {
- if (query.HasThemeVideo.Value)
- {
- whereClauses.Add("ThemeVideoIds not null");
- }
- else
- {
- whereClauses.Add("ThemeVideoIds is null");
- }
- }
var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList();
var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
- var queryTopParentIds = query.TopParentIds.Where(IsValidId).ToArray();
+ var queryTopParentIds = query.TopParentIds;
if (queryTopParentIds.Length == 1)
{
@@ -4565,12 +4795,12 @@ namespace Emby.Server.Implementations.Data
}
if (statement != null)
{
- statement.TryBind("@TopParentId", queryTopParentIds[0]);
+ statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N"));
}
}
else if (queryTopParentIds.Length > 1)
{
- var val = string.Join(",", queryTopParentIds.Select(i => "'" + i + "'").ToArray());
+ var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N") + "'").ToArray());
if (enableItemsByName && includedItemByNameTypes.Count == 1)
{
@@ -4597,12 +4827,12 @@ namespace Emby.Server.Implementations.Data
if (statement != null)
{
- statement.TryBind("@AncestorId", new Guid(query.AncestorIds[0]));
+ statement.TryBind("@AncestorId", query.AncestorIds[0]);
}
}
if (query.AncestorIds.Length > 1)
{
- var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + new Guid(i).ToString("N") + "'").ToArray());
+ var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N") + "'").ToArray());
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
}
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
@@ -4647,6 +4877,114 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + tagValuesList + ")) is null)");
}
+ if (query.SeriesStatuses.Length > 0)
+ {
+ var statuses = new List<string>();
+
+ foreach (var seriesStatus in query.SeriesStatuses)
+ {
+ statuses.Add("data like '%" + seriesStatus + "%'");
+ }
+
+ whereClauses.Add("(" + string.Join(" OR ", statuses.ToArray()) + ")");
+ }
+
+ if (query.BoxSetLibraryFolders.Length > 0)
+ {
+ var folderIdQueries = new List<string>();
+
+ foreach (var folderId in query.BoxSetLibraryFolders)
+ {
+ folderIdQueries.Add("data like '%" + folderId.ToString("N") + "%'");
+ }
+
+ whereClauses.Add("(" + string.Join(" OR ", folderIdQueries.ToArray()) + ")");
+ }
+
+ if (query.VideoTypes.Length > 0)
+ {
+ var videoTypes = new List<string>();
+
+ foreach (var videoType in query.VideoTypes)
+ {
+ videoTypes.Add("data like '%\"VideoType\":\"" + videoType.ToString() + "\"%'");
+ }
+
+ whereClauses.Add("(" + string.Join(" OR ", videoTypes.ToArray()) + ")");
+ }
+
+ if (query.Is3D.HasValue)
+ {
+ if (query.Is3D.Value)
+ {
+ whereClauses.Add("data like '%Video3DFormat%'");
+ }
+ else
+ {
+ whereClauses.Add("data not like '%Video3DFormat%'");
+ }
+ }
+
+ if (query.IsPlaceHolder.HasValue)
+ {
+ if (query.IsPlaceHolder.Value)
+ {
+ whereClauses.Add("data like '%\"IsPlaceHolder\":true%'");
+ }
+ else
+ {
+ whereClauses.Add("(data is null or data not like '%\"IsPlaceHolder\":true%')");
+ }
+ }
+
+ if (query.HasSpecialFeature.HasValue)
+ {
+ if (query.HasSpecialFeature.Value)
+ {
+ whereClauses.Add("ExtraIds not null");
+ }
+ else
+ {
+ whereClauses.Add("ExtraIds is null");
+ }
+ }
+
+ if (query.HasTrailer.HasValue)
+ {
+ if (query.HasTrailer.Value)
+ {
+ whereClauses.Add("ExtraIds not null");
+ }
+ else
+ {
+ whereClauses.Add("ExtraIds is null");
+ }
+ }
+
+ if (query.HasThemeSong.HasValue)
+ {
+ if (query.HasThemeSong.Value)
+ {
+ whereClauses.Add("ExtraIds not null");
+ }
+ else
+ {
+ whereClauses.Add("ExtraIds is null");
+ }
+ }
+
+ if (query.HasThemeVideo.HasValue)
+ {
+ if (query.HasThemeVideo.Value)
+ {
+ whereClauses.Add("ExtraIds not null");
+ }
+ else
+ {
+ whereClauses.Add("ExtraIds is null");
+ }
+ }
+
return whereClauses;
}
@@ -4749,8 +5087,6 @@ namespace Emby.Server.Implementations.Data
{
typeof(LiveTvProgram),
typeof(LiveTvChannel),
- typeof(LiveTvVideoRecording),
- typeof(LiveTvAudioRecording),
typeof(Series),
typeof(Audio),
typeof(MusicAlbum),
@@ -4759,7 +5095,6 @@ namespace Emby.Server.Implementations.Data
typeof(MusicVideo),
typeof(Movie),
typeof(Playlist),
- typeof(AudioPodcast),
typeof(AudioBook),
typeof(Trailer),
typeof(BoxSet),
@@ -4825,7 +5160,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
dict[t.Name] = new[] { t.FullName };
}
- dict["Recording"] = new[] { typeof(LiveTvAudioRecording).FullName, typeof(LiveTvVideoRecording).FullName };
dict["Program"] = new[] { typeof(LiveTvProgram).FullName };
dict["TvChannel"] = new[] { typeof(LiveTvChannel).FullName };
@@ -4848,7 +5182,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
public void DeleteItem(Guid id, CancellationToken cancellationToken)
{
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -4861,23 +5195,25 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
connection.RunInTransaction(db =>
{
+ var idBlob = id.ToGuidBlob();
+
// Delete people
- ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob);
// Delete chapters
- ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", idBlob);
// Delete media streams
- ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", idBlob);
// Delete ancestors
- ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", idBlob);
// Delete item values
- ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", idBlob);
// Delete the item
- ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidBlob());
+ ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob);
}, TransactionMode);
}
}
@@ -4979,7 +5315,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var whereClauses = new List<string>();
- if (query.ItemId != Guid.Empty)
+ if (!query.ItemId.Equals(Guid.Empty))
{
whereClauses.Add("ItemId=@ItemId");
if (statement != null)
@@ -4987,7 +5323,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
}
}
- if (query.AppearsInItemId != Guid.Empty)
+ if (!query.AppearsInItemId.Equals(Guid.Empty))
{
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
if (statement != null)
@@ -5047,9 +5383,9 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return whereClauses;
}
- private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDatabaseConnection db, IStatement deleteAncestorsStatement, IStatement updateAncestorsStatement)
+ private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDatabaseConnection db, IStatement deleteAncestorsStatement)
{
- if (itemId == Guid.Empty)
+ if (itemId.Equals(Guid.Empty))
{
throw new ArgumentNullException("itemId");
}
@@ -5061,18 +5397,46 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
CheckDisposed();
+ var itemIdBlob = itemId.ToGuidBlob();
+
// First delete
deleteAncestorsStatement.Reset();
- deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
+ deleteAncestorsStatement.TryBind("@ItemId", itemIdBlob);
deleteAncestorsStatement.MoveNext();
- foreach (var ancestorId in ancestorIds)
+ if (ancestorIds.Count == 0)
{
- updateAncestorsStatement.Reset();
- updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
- updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidBlob());
- updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
- updateAncestorsStatement.MoveNext();
+ return;
+ }
+
+ var insertText = new StringBuilder("insert into AncestorIds (ItemId, AncestorId, AncestorIdText) values ");
+
+ for (var i = 0; i < ancestorIds.Count; i++)
+ {
+ if (i > 0)
+ {
+ insertText.Append(",");
+ }
+
+ insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture));
+ }
+
+ using (var statement = PrepareStatementSafe(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", itemIdBlob);
+
+ for (var i = 0; i < ancestorIds.Count; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var ancestorId = ancestorIds[i];
+
+ statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob());
+ statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N"));
+ }
+
+ statement.Reset();
+ statement.MoveNext();
}
}
@@ -5249,18 +5613,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var columns = _retriveItemColumns.ToList();
columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
- columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList();
-
- var commandText = "select " + string.Join(",", columns.ToArray()) + GetFromText();
- commandText += GetJoinUserDataText(query);
-
+ // do this first before calling GetFinalColumnsToSelect, otherwise ExcludeItemIds will be set by SimilarTo
var innerQuery = new InternalItemsQuery(query.User)
{
ExcludeItemTypes = query.ExcludeItemTypes,
IncludeItemTypes = query.IncludeItemTypes,
MediaTypes = query.MediaTypes,
AncestorIds = query.AncestorIds,
- ExcludeItemIds = query.ExcludeItemIds,
ItemIds = query.ItemIds,
TopParentIds = query.TopParentIds,
ParentId = query.ParentId,
@@ -5273,6 +5632,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
IsSeries = query.IsSeries
};
+ columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList();
+
+ var commandText = "select " + string.Join(",", columns.ToArray()) + GetFromText();
+ commandText += GetJoinUserDataText(query);
+
var innerWhereClauses = GetWhereClauses(innerQuery, null);
var innerWhereText = innerWhereClauses.Count == 0 ?
@@ -5305,7 +5669,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
GenreIds = query.GenreIds,
Genres = query.Genres,
Years = query.Years,
- NameContains = query.NameContains
+ NameContains = query.NameContains,
+ SearchTerm = query.SearchTerm,
+ SimilarTo = query.SimilarTo,
+ ExcludeItemIds = query.ExcludeItemIds
};
var outerWhereClauses = GetWhereClauses(outerQuery, null);
@@ -5318,7 +5685,14 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
commandText += whereText;
commandText += " group by PresentationUniqueKey";
- commandText += " order by SortName";
+ if (query.SimilarTo != null || !string.IsNullOrEmpty(query.SearchTerm))
+ {
+ commandText += GetOrderByText(query);
+ }
+ else
+ {
+ commandText += " order by SortName";
+ }
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
@@ -5344,7 +5718,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
if (query.EnableTotalRecordCount)
{
- var countText = "select count (distinct PresentationUniqueKey)" + GetFromText();
+ var countText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
countText += GetJoinUserDataText(query);
countText += whereText;
@@ -5360,6 +5734,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var list = new List<Tuple<BaseItem, ItemCounts>>();
var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
+ //Logger.Info("GetItemValues {0}", string.Join(";", statementTexts.ToArray()));
var statements = PrepareAllSafe(db, statementTexts);
if (!isReturningZeroItems)
@@ -5369,7 +5744,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@SelectType", returnType);
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
if (typeSubQuery != null)
@@ -5377,11 +5752,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
GetWhereClauses(typeSubQuery, null);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
GetWhereClauses(innerQuery, statement);
GetWhereClauses(outerQuery, statement);
var hasEpisodeAttributes = HasEpisodeAttributes(query);
var hasProgramAttributes = HasProgramAttributes(query);
+ var hasServiceName = HasServiceName(query);
var hasStartDate = HasStartDate(query);
var hasTrailerTypes = HasTrailerTypes(query);
var hasArtistFields = HasArtistFields(query);
@@ -5389,7 +5766,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
foreach (var row in statement.ExecuteQuery())
{
- var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
+ var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
if (item != null)
{
var countStartColumn = columns.Count - 1;
@@ -5404,7 +5781,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
if (query.EnableTotalRecordCount)
{
- commandText = "select count (distinct PresentationUniqueKey)" + GetFromText();
+ commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
commandText += GetJoinUserDataText(query);
commandText += whereText;
@@ -5414,7 +5791,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@SelectType", returnType);
if (EnableJoinUserData(query))
{
- statement.TryBind("@UserId", query.User.Id);
+ statement.TryBind("@UserId", query.User.InternalId);
}
if (typeSubQuery != null)
@@ -5422,6 +5799,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
GetWhereClauses(typeSubQuery, null);
}
BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
GetWhereClauses(innerQuery, statement);
GetWhereClauses(outerQuery, statement);
@@ -5535,7 +5913,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
private void UpdateItemValues(Guid itemId, List<Tuple<int, string>> values, IDatabaseConnection db)
{
- if (itemId == Guid.Empty)
+ if (itemId.Equals(Guid.Empty))
{
throw new ArgumentNullException("itemId");
}
@@ -5552,41 +5930,66 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
// First delete
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)"))
+ InsertItemValues(guidBlob, values, db);
+ }
+
+ private void InsertItemValues(byte[] idBlob, List<Tuple<int, string>> values, IDatabaseConnection db)
+ {
+ var startIndex = 0;
+ var limit = 100;
+
+ while (startIndex < values.Count)
{
- foreach (var pair in values)
- {
- var itemValue = pair.Item2;
+ var insertText = new StringBuilder("insert into ItemValues (ItemId, Type, Value, CleanValue) values ");
- // Don't save if invalid
- if (string.IsNullOrWhiteSpace(itemValue))
+ var endIndex = Math.Min(values.Count, startIndex + limit);
+ var isSubsequentRow = false;
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ if (isSubsequentRow)
{
- continue;
+ insertText.Append(",");
}
- statement.Reset();
+ insertText.AppendFormat("(@ItemId, @Type{0}, @Value{0}, @CleanValue{0})", i.ToString(CultureInfo.InvariantCulture));
+ isSubsequentRow = true;
+ }
- statement.TryBind("@ItemId", guidBlob);
- statement.TryBind("@Type", pair.Item1);
- statement.TryBind("@Value", itemValue);
+ using (var statement = PrepareStatementSafe(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", idBlob);
- if (pair.Item2 == null)
+ for (var i = startIndex; i < endIndex; i++)
{
- statement.TryBindNull("@CleanValue");
- }
- else
- {
- statement.TryBind("@CleanValue", GetCleanValue(pair.Item2));
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var currentValueInfo = values[i];
+
+ var itemValue = currentValueInfo.Item2;
+
+ // Don't save if invalid
+ if (string.IsNullOrWhiteSpace(itemValue))
+ {
+ continue;
+ }
+
+ statement.TryBind("@Type" + index, currentValueInfo.Item1);
+ statement.TryBind("@Value" + index, itemValue);
+ statement.TryBind("@CleanValue" + index, GetCleanValue(itemValue));
}
+ statement.Reset();
statement.MoveNext();
}
+
+ startIndex += limit;
}
}
public void UpdatePeople(Guid itemId, List<PersonInfo> people)
{
- if (itemId == Guid.Empty)
+ if (itemId.Equals(Guid.Empty))
{
throw new ArgumentNullException("itemId");
}
@@ -5602,34 +6005,69 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
using (var connection = CreateConnection())
{
- // First delete
- // "delete from People where ItemId=?"
- connection.Execute("delete from People where ItemId=?", itemId.ToGuidBlob());
+ connection.RunInTransaction(db =>
+ {
+ var itemIdBlob = itemId.ToGuidBlob();
+
+ // First delete chapters
+ db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
+
+ InsertPeople(itemIdBlob, people, db);
+
+ }, TransactionMode);
+
+ }
+ }
+ }
+
+ private void InsertPeople(byte[] idBlob, List<PersonInfo> people, IDatabaseConnection db)
+ {
+ var startIndex = 0;
+ var limit = 100;
+ var listIndex = 0;
- var listIndex = 0;
+ while (startIndex < people.Count)
+ {
+ var insertText = new StringBuilder("insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values ");
+
+ var endIndex = Math.Min(people.Count, startIndex + limit);
+ var isSubsequentRow = false;
- using (var statement = PrepareStatement(connection,
- "insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values (@ItemId, @Name, @Role, @PersonType, @SortOrder, @ListOrder)"))
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ if (isSubsequentRow)
{
- foreach (var person in people)
- {
- if (listIndex > 0)
- {
- statement.Reset();
- }
+ insertText.Append(",");
+ }
- statement.TryBind("@ItemId", itemId.ToGuidBlob());
- statement.TryBind("@Name", person.Name);
- statement.TryBind("@Role", person.Role);
- statement.TryBind("@PersonType", person.Type);
- statement.TryBind("@SortOrder", person.SortOrder);
- statement.TryBind("@ListOrder", listIndex);
+ insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0})", i.ToString(CultureInfo.InvariantCulture));
+ isSubsequentRow = true;
+ }
- statement.MoveNext();
- listIndex++;
- }
+ using (var statement = PrepareStatementSafe(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", idBlob);
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var person = people[i];
+
+ statement.TryBind("@Name" + index, person.Name);
+ statement.TryBind("@Role" + index, person.Role);
+ statement.TryBind("@PersonType" + index, person.Type);
+ statement.TryBind("@SortOrder" + index, person.SortOrder);
+ statement.TryBind("@ListOrder" + index, listIndex);
+
+ listIndex++;
}
+
+ statement.Reset();
+ statement.MoveNext();
}
+
+ startIndex += limit;
}
}
@@ -5718,7 +6156,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
CheckDisposed();
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -5734,62 +6172,111 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
using (var connection = CreateConnection())
{
- // First delete chapters
- connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidBlob());
+ connection.RunInTransaction(db =>
+ {
+ var itemIdBlob = id.ToGuidBlob();
+
+ // First delete chapters
+ db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
+
+ InsertMediaStreams(itemIdBlob, streams, db);
- using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
- string.Join(",", _mediaStreamSaveColumns),
- string.Join(",", _mediaStreamSaveColumns.Select(i => "@" + i).ToArray()))))
+ }, TransactionMode);
+ }
+ }
+ }
+
+ private void InsertMediaStreams(byte[] idBlob, List<MediaStream> streams, IDatabaseConnection db)
+ {
+ var startIndex = 0;
+ var limit = 10;
+
+ while (startIndex < streams.Count)
+ {
+ var insertText = new StringBuilder(string.Format("insert into mediastreams ({0}) values ", string.Join(",", _mediaStreamSaveColumns)));
+
+ var endIndex = Math.Min(streams.Count, startIndex + limit);
+ var isSubsequentRow = false;
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ if (isSubsequentRow)
{
- foreach (var stream in streams)
- {
- var paramList = new List<object>();
-
- paramList.Add(id.ToGuidBlob());
- paramList.Add(stream.Index);
- paramList.Add(stream.Type.ToString());
- paramList.Add(stream.Codec);
- paramList.Add(stream.Language);
- paramList.Add(stream.ChannelLayout);
- paramList.Add(stream.Profile);
- paramList.Add(stream.AspectRatio);
- paramList.Add(stream.Path);
-
- paramList.Add(stream.IsInterlaced);
- paramList.Add(stream.BitRate);
- paramList.Add(stream.Channels);
- paramList.Add(stream.SampleRate);
-
- paramList.Add(stream.IsDefault);
- paramList.Add(stream.IsForced);
- paramList.Add(stream.IsExternal);
-
- paramList.Add(stream.Width);
- paramList.Add(stream.Height);
- paramList.Add(stream.AverageFrameRate);
- paramList.Add(stream.RealFrameRate);
- paramList.Add(stream.Level);
- paramList.Add(stream.PixelFormat);
- paramList.Add(stream.BitDepth);
- paramList.Add(stream.IsExternal);
- paramList.Add(stream.RefFrames);
-
- paramList.Add(stream.CodecTag);
- paramList.Add(stream.Comment);
- paramList.Add(stream.NalLengthSize);
- paramList.Add(stream.IsAVC);
- paramList.Add(stream.Title);
-
- paramList.Add(stream.TimeBase);
- paramList.Add(stream.CodecTimeBase);
-
- statement.Execute(paramList.ToArray());
- }
+ insertText.Append(",");
+ }
+
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var mediaStreamSaveColumns = string.Join(",", _mediaStreamSaveColumns.Skip(1).Select(m => "@" + m + index).ToArray());
+
+ insertText.AppendFormat("(@ItemId, {0})", mediaStreamSaveColumns);
+ isSubsequentRow = true;
+ }
+
+ using (var statement = PrepareStatementSafe(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", idBlob);
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var stream = streams[i];
+
+ statement.TryBind("@StreamIndex" + index, stream.Index);
+ statement.TryBind("@StreamType" + index, stream.Type.ToString());
+ statement.TryBind("@Codec" + index, stream.Codec);
+ statement.TryBind("@Language" + index, stream.Language);
+ statement.TryBind("@ChannelLayout" + index, stream.ChannelLayout);
+ statement.TryBind("@Profile" + index, stream.Profile);
+ statement.TryBind("@AspectRatio" + index, stream.AspectRatio);
+ statement.TryBind("@Path" + index, GetPathToSave(stream.Path));
+
+ statement.TryBind("@IsInterlaced" + index, stream.IsInterlaced);
+ statement.TryBind("@BitRate" + index, stream.BitRate);
+ statement.TryBind("@Channels" + index, stream.Channels);
+ statement.TryBind("@SampleRate" + index, stream.SampleRate);
+
+ statement.TryBind("@IsDefault" + index, stream.IsDefault);
+ statement.TryBind("@IsForced" + index, stream.IsForced);
+ statement.TryBind("@IsExternal" + index, stream.IsExternal);
+
+ // Yes these are backwards due to a mistake
+ statement.TryBind("@Width" + index, stream.Height);
+ statement.TryBind("@Height" + index, stream.Width);
+
+ statement.TryBind("@AverageFrameRate" + index, stream.AverageFrameRate);
+ statement.TryBind("@RealFrameRate" + index, stream.RealFrameRate);
+ statement.TryBind("@Level" + index, stream.Level);
+
+ statement.TryBind("@PixelFormat" + index, stream.PixelFormat);
+ statement.TryBind("@BitDepth" + index, stream.BitDepth);
+ statement.TryBind("@IsExternal" + index, stream.IsExternal);
+ statement.TryBind("@RefFrames" + index, stream.RefFrames);
+
+ statement.TryBind("@CodecTag" + index, stream.CodecTag);
+ statement.TryBind("@Comment" + index, stream.Comment);
+ statement.TryBind("@NalLengthSize" + index, stream.NalLengthSize);
+ statement.TryBind("@IsAvc" + index, stream.IsAVC);
+ statement.TryBind("@Title" + index, stream.Title);
+
+ statement.TryBind("@TimeBase" + index, stream.TimeBase);
+ statement.TryBind("@CodecTimeBase" + index, stream.CodecTimeBase);
+
+ statement.TryBind("@ColorPrimaries" + index, stream.ColorPrimaries);
+ statement.TryBind("@ColorSpace" + index, stream.ColorSpace);
+ statement.TryBind("@ColorTransfer" + index, stream.ColorTransfer);
}
+
+ statement.Reset();
+ statement.MoveNext();
}
+
+ startIndex += limit;
}
}
+
/// <summary>
/// Gets the chapter.
/// </summary>
@@ -5831,7 +6318,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
if (reader[8].SQLiteType != SQLiteType.Null)
{
- item.Path = reader[8].ToString();
+ item.Path = RestorePath(reader[8].ToString());
}
item.IsInterlaced = reader.GetBoolean(9);
@@ -5935,6 +6422,21 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
item.CodecTimeBase = reader[31].ToString();
}
+ if (reader[32].SQLiteType != SQLiteType.Null)
+ {
+ item.ColorPrimaries = reader[32].ToString();
+ }
+
+ if (reader[33].SQLiteType != SQLiteType.Null)
+ {
+ item.ColorSpace = reader[33].ToString();
+ }
+
+ if (reader[34].SQLiteType != SQLiteType.Null)
+ {
+ item.ColorTransfer = reader[34].ToString();
+ }
+
return item;
}
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index ad5c60ede..07d64a2b0 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -9,12 +9,12 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using SQLitePCL.pretty;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.Data
{
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
- private readonly string _importFile;
private readonly IFileSystem _fileSystem;
public SqliteUserDataRepository(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem)
@@ -22,7 +22,6 @@ namespace Emby.Server.Implementations.Data
{
_fileSystem = fileSystem;
DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
- _importFile = Path.Combine(appPaths.DataPath, "userdata_v2.db");
}
/// <summary>
@@ -41,7 +40,7 @@ namespace Emby.Server.Implementations.Data
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public void Initialize(ReaderWriterLockSlim writeLock, ManagedConnection managedConnection)
+ public void Initialize(ReaderWriterLockSlim writeLock, ManagedConnection managedConnection, IUserManager userManager)
{
_connection = managedConnection;
@@ -50,138 +49,134 @@ namespace Emby.Server.Implementations.Data
using (var connection = CreateConnection())
{
- string[] queries = {
+ var userDatasTableExists = TableExists(connection, "UserDatas");
+ var userDataTableExists = TableExists(connection, "userdata");
- "create table if not exists userdata (key nvarchar not null, userId GUID not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null)",
-
- "create table if not exists DataSettings (IsUserDataImported bit)",
-
- "drop index if exists idx_userdata",
- "drop index if exists idx_userdata1",
- "drop index if exists idx_userdata2",
- "drop index if exists userdataindex1",
-
- "create unique index if not exists userdataindex on userdata (key, userId)",
- "create index if not exists userdataindex2 on userdata (key, userId, played)",
- "create index if not exists userdataindex3 on userdata (key, userId, playbackPositionTicks)",
- "create index if not exists userdataindex4 on userdata (key, userId, isFavorite)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries);
+ var users = userDatasTableExists ? null : userManager.Users.ToArray();
connection.RunInTransaction(db =>
{
- var existingColumnNames = GetColumnNames(db, "userdata");
+ db.ExecuteAll(string.Join(";", new[] {
+
+ "create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
+
+ "drop index if exists idx_userdata",
+ "drop index if exists idx_userdata1",
+ "drop index if exists idx_userdata2",
+ "drop index if exists userdataindex1",
+ "drop index if exists userdataindex",
+ "drop index if exists userdataindex3",
+ "drop index if exists userdataindex4",
+ "create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
+ "create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
+ "create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
+ "create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)"
+ }));
+
+ if (userDataTableExists)
+ {
+ var existingColumnNames = GetColumnNames(db, "userdata");
- AddColumn(db, "userdata", "AudioStreamIndex", "int", existingColumnNames);
- AddColumn(db, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
- }, TransactionMode);
+ AddColumn(db, "userdata", "InternalUserId", "int", existingColumnNames);
+ AddColumn(db, "userdata", "AudioStreamIndex", "int", existingColumnNames);
+ AddColumn(db, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
- try
- {
- ImportUserDataIfNeeded(connection);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in ImportUserDataIfNeeded", ex);
- }
- }
- }
+ if (!userDatasTableExists)
+ {
+ ImportUserIds(db, users);
- protected override bool EnableTempStoreMemory
- {
- get
- {
- return true;
+ db.ExecuteAll("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
+ }
+ }
+ }, TransactionMode);
}
}
- private void ImportUserDataIfNeeded(ManagedConnection connection)
+ private void ImportUserIds(IDatabaseConnection db, User[] users)
{
- if (!_fileSystem.FileExists(_importFile))
+ var userIdsWithUserData = GetAllUserIdsWithUserData(db);
+
+ using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=@UserId"))
{
- return;
- }
+ foreach (var user in users)
+ {
+ if (!userIdsWithUserData.Contains(user.Id))
+ {
+ continue;
+ }
- var fileToImport = _importFile;
- var isImported = connection.Query("select IsUserDataImported from DataSettings").SelectScalarBool().FirstOrDefault();
+ statement.TryBind("@UserId", user.Id.ToGuidBlob());
+ statement.TryBind("@InternalUserId", user.InternalId);
- if (isImported)
- {
- return;
+ statement.MoveNext();
+ statement.Reset();
+ }
}
+ }
- ImportUserData(connection, fileToImport);
+ private List<Guid> GetAllUserIdsWithUserData(IDatabaseConnection db)
+ {
+ List<Guid> list = new List<Guid>();
- connection.RunInTransaction(db =>
+ using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
{
- using (var statement = db.PrepareStatement("replace into DataSettings (IsUserDataImported) values (@IsUserDataImported)"))
+ foreach (var row in statement.ExecuteQuery())
{
- statement.TryBind("@IsUserDataImported", true);
- statement.MoveNext();
+ try
+ {
+ list.Add(row[0].ReadGuidFromBlob());
+ }
+ catch
+ {
+
+ }
}
- }, TransactionMode);
+ }
+
+ return list;
}
- private void ImportUserData(ManagedConnection connection, string file)
+ protected override bool EnableTempStoreMemory
{
- SqliteExtensions.Attach(connection, file, "UserDataBackup");
-
- var columns = "key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex";
-
- connection.RunInTransaction(db =>
+ get
{
- db.Execute("REPLACE INTO userdata(" + columns + ") SELECT " + columns + " FROM UserDataBackup.userdata;");
- }, TransactionMode);
+ return true;
+ }
}
/// <summary>
/// Saves the user data.
/// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="key">The key.</param>
- /// <param name="userData">The user data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">userData
- /// or
- /// cancellationToken
- /// or
- /// userId
- /// or
- /// userDataId</exception>
- public void SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+ public void SaveUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException("userData");
}
- if (userId == Guid.Empty)
+ if (internalUserId <= 0)
{
- throw new ArgumentNullException("userId");
+ throw new ArgumentNullException("internalUserId");
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
- PersistUserData(userId, key, userData, cancellationToken);
+ PersistUserData(internalUserId, key, userData, cancellationToken);
}
- public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
+ public void SaveAllUserData(long internalUserId, UserItemData[] userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException("userData");
}
- if (userId == Guid.Empty)
+ if (internalUserId <= 0)
{
- throw new ArgumentNullException("userId");
+ throw new ArgumentNullException("internalUserId");
}
- PersistAllUserData(userId, userData, cancellationToken);
+ PersistAllUserData(internalUserId, userData, cancellationToken);
}
/// <summary>
@@ -192,7 +187,7 @@ namespace Emby.Server.Implementations.Data
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- public void PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+ public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -202,17 +197,17 @@ namespace Emby.Server.Implementations.Data
{
connection.RunInTransaction(db =>
{
- SaveUserData(db, userId, key, userData);
+ SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
}
}
}
- private void SaveUserData(IDatabaseConnection db, Guid userId, string key, UserItemData userData)
+ private void SaveUserData(IDatabaseConnection db, long internalUserId, string key, UserItemData userData)
{
- using (var statement = db.PrepareStatement("replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
+ using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
{
- statement.TryBind("@userId", userId.ToGuidBlob());
+ statement.TryBind("@userId", internalUserId);
statement.TryBind("@key", key);
if (userData.Rating.HasValue)
@@ -263,7 +258,7 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Persist all user data for the specified user
/// </summary>
- private void PersistAllUserData(Guid userId, UserItemData[] userDataList, CancellationToken cancellationToken)
+ private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -275,7 +270,7 @@ namespace Emby.Server.Implementations.Data
{
foreach (var userItemData in userDataList)
{
- SaveUserData(db, userId, userItemData.Key, userItemData);
+ SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
}, TransactionMode);
}
@@ -293,11 +288,11 @@ namespace Emby.Server.Implementations.Data
/// or
/// key
/// </exception>
- public UserItemData GetUserData(Guid userId, string key)
+ public UserItemData GetUserData(long internalUserId, string key)
{
- if (userId == Guid.Empty)
+ if (internalUserId <= 0)
{
- throw new ArgumentNullException("userId");
+ throw new ArgumentNullException("internalUserId");
}
if (string.IsNullOrEmpty(key))
{
@@ -308,9 +303,9 @@ namespace Emby.Server.Implementations.Data
{
using (var connection = CreateConnection(true))
{
- using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key =@Key and userId=@UserId"))
+ using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
{
- statement.TryBind("@UserId", userId.ToGuidBlob());
+ statement.TryBind("@UserId", internalUserId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
@@ -324,12 +319,8 @@ namespace Emby.Server.Implementations.Data
}
}
- public UserItemData GetUserData(Guid userId, List<string> keys)
+ public UserItemData GetUserData(long internalUserId, List<string> keys)
{
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
if (keys == null)
{
throw new ArgumentNullException("keys");
@@ -340,7 +331,7 @@ namespace Emby.Server.Implementations.Data
return null;
}
- return GetUserData(userId, keys[0]);
+ return GetUserData(internalUserId, keys[0]);
}
/// <summary>
@@ -348,11 +339,11 @@ namespace Emby.Server.Implementations.Data
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
- public List<UserItemData> GetAllUserData(Guid userId)
+ public List<UserItemData> GetAllUserData(long internalUserId)
{
- if (userId == Guid.Empty)
+ if (internalUserId <= 0)
{
- throw new ArgumentNullException("userId");
+ throw new ArgumentNullException("internalUserId");
}
var list = new List<UserItemData>();
@@ -361,9 +352,9 @@ namespace Emby.Server.Implementations.Data
{
using (var connection = CreateConnection())
{
- using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@UserId"))
+ using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
- statement.TryBind("@UserId", userId.ToGuidBlob());
+ statement.TryBind("@UserId", internalUserId);
foreach (var row in statement.ExecuteQuery())
{
@@ -385,7 +376,7 @@ namespace Emby.Server.Implementations.Data
var userData = new UserItemData();
userData.Key = reader[0].ToString();
- userData.UserId = reader[1].ReadGuidFromBlob();
+ //userData.UserId = reader[1].ReadGuidFromBlob();
if (reader[2].SQLiteType != SQLiteType.Null)
{
@@ -399,7 +390,7 @@ namespace Emby.Server.Implementations.Data
if (reader[7].SQLiteType != SQLiteType.Null)
{
- userData.LastPlayedDate = reader[7].ReadDateTime();
+ userData.LastPlayedDate = reader[7].TryReadDateTime();
}
if (reader[8].SQLiteType != SQLiteType.Null)
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index e89de11c6..da828aa11 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -18,13 +18,11 @@ namespace Emby.Server.Implementations.Data
public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
{
private readonly IJsonSerializer _jsonSerializer;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
- public SqliteUserRepository(ILogger logger, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamProvider)
+ public SqliteUserRepository(ILogger logger, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer)
: base(logger)
{
_jsonSerializer = jsonSerializer;
- _memoryStreamProvider = memoryStreamProvider;
DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
}
@@ -51,37 +49,83 @@ namespace Emby.Server.Implementations.Data
{
RunDefaultInitialization(connection);
- string[] queries = {
+ var localUsersTableExists = TableExists(connection, "LocalUsersv2");
- "create table if not exists users (guid GUID primary key NOT NULL, data BLOB NOT NULL)",
- "create index if not exists idx_users on users(guid)",
+ connection.RunQueries(new[] {
+ "create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
+ "drop index if exists idx_users"
+ });
- "pragma shrink_memory"
- };
+ if (!localUsersTableExists && TableExists(connection, "Users"))
+ {
+ TryMigrateToLocalUsersTable(connection);
+ }
+ }
+ }
- connection.RunQueries(queries);
+ private void TryMigrateToLocalUsersTable(ManagedConnection connection)
+ {
+ try
+ {
+ connection.RunQueries(new[]
+ {
+ "INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
+ });
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error migrating users database", ex);
}
}
/// <summary>
/// Save a user in the repo
/// </summary>
- /// <param name="user">The user.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public void SaveUser(User user, CancellationToken cancellationToken)
+ public void CreateUser(User user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
- cancellationToken.ThrowIfCancellationRequested();
+ var serialized = _jsonSerializer.SerializeToBytes(user);
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
+ {
+ statement.TryBind("@guid", user.Id.ToGuidBlob());
+ statement.TryBind("@data", serialized);
+
+ statement.MoveNext();
+ }
- var serialized = _jsonSerializer.SerializeToBytes(user, _memoryStreamProvider);
+ var createdUser = GetUser(user.Id, false);
- cancellationToken.ThrowIfCancellationRequested();
+ if (createdUser == null)
+ {
+ throw new ApplicationException("created user should never be null");
+ }
+
+ user.InternalId = createdUser.InternalId;
+
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public void UpdateUser(User user)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException("user");
+ }
+
+ var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write())
{
@@ -89,22 +133,59 @@ namespace Emby.Server.Implementations.Data
{
connection.RunInTransaction(db =>
{
- using (var statement = db.PrepareStatement("replace into users (guid, data) values (@guid, @data)"))
+ using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
- statement.TryBind("@guid", user.Id.ToGuidBlob());
+ statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
+
}, TransactionMode);
}
}
}
+ private User GetUser(Guid guid, bool openLock)
+ {
+ using (openLock ? WriteLock.Read() : null)
+ {
+ using (var connection = CreateConnection(true))
+ {
+ using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
+ {
+ statement.TryBind("@guid", guid);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return GetUser(row);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private User GetUser(IReadOnlyList<IResultSetValue> row)
+ {
+ var id = row[0].ToInt64();
+ var guid = row[1].ReadGuidFromBlob();
+
+ using (var stream = new MemoryStream(row[2].ToBlob()))
+ {
+ stream.Position = 0;
+ var user = _jsonSerializer.DeserializeFromStream<User>(stream);
+ user.InternalId = id;
+ user.Id = guid;
+ return user;
+ }
+ }
+
/// <summary>
/// Retrieve all users from the database
/// </summary>
/// <returns>IEnumerable{User}.</returns>
- public IEnumerable<User> RetrieveAllUsers()
+ public List<User> RetrieveAllUsers()
{
var list = new List<User>();
@@ -112,17 +193,9 @@ namespace Emby.Server.Implementations.Data
{
using (var connection = CreateConnection(true))
{
- foreach (var row in connection.Query("select guid,data from users"))
+ foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
{
- var id = row[0].ReadGuidFromBlob();
-
- using (var stream = _memoryStreamProvider.CreateNew(row[1].ToBlob()))
- {
- stream.Position = 0;
- var user = _jsonSerializer.DeserializeFromStream<User>(stream);
- user.Id = id;
- list.Add(user);
- }
+ list.Add(GetUser(row));
}
}
}
@@ -137,24 +210,22 @@ namespace Emby.Server.Implementations.Data
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">user</exception>
- public void DeleteUser(User user, CancellationToken cancellationToken)
+ public void DeleteUser(User user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
- cancellationToken.ThrowIfCancellationRequested();
-
using (WriteLock.Write())
{
using (var connection = CreateConnection())
{
connection.RunInTransaction(db =>
{
- using (var statement = db.PrepareStatement("delete from users where guid=@id"))
+ using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
{
- statement.TryBind("@id", user.Id.ToGuidBlob());
+ statement.TryBind("@id", user.InternalId);
statement.MoveNext();
}
}, TransactionMode);
diff --git a/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs b/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs
deleted file mode 100644
index bb9ef157c..000000000
--- a/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace Emby.Server.Implementations.Devices
-{
- public class CameraUploadsDynamicFolder : IVirtualFolderCreator
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
-
- public CameraUploadsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem)
- {
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- }
-
- public BasePluginFolder GetFolder()
- {
- var path = Path.Combine(_appPaths.DataPath, "camerauploads");
-
- _fileSystem.CreateDirectory(path);
-
- return new CameraUploadsFolder
- {
- Path = path
- };
- }
- }
-
-}
diff --git a/Emby.Server.Implementations/Devices/CameraUploadsFolder.cs b/Emby.Server.Implementations/Devices/CameraUploadsFolder.cs
deleted file mode 100644
index 5c205dd19..000000000
--- a/Emby.Server.Implementations/Devices/CameraUploadsFolder.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace Emby.Server.Implementations.Devices
-{
- public class CameraUploadsFolder : BasePluginFolder, ISupportsUserSpecificView
- {
- public CameraUploadsFolder()
- {
- Name = "Camera Uploads";
- }
-
- public override bool IsVisible(User user)
- {
- if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsVisible(user) && HasChildren();
- }
-
- [IgnoreDataMember]
- public override string CollectionType
- {
- get { return MediaBrowser.Model.Entities.CollectionType.HomeVideos; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- public override string GetClientTypeName()
- {
- return typeof(CollectionFolder).Name;
- }
-
- private bool? _hasChildren;
- private bool HasChildren()
- {
- if (!_hasChildren.HasValue)
- {
- _hasChildren = LibraryManager.GetItemIds(new InternalItemsQuery { Parent = this }).Count > 0;
- }
-
- return _hasChildren.Value;
- }
-
- protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- _hasChildren = null;
- return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
- }
-
- [IgnoreDataMember]
- public bool EnableUserSpecificView
- {
- get { return true; }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index ee4c4bb26..0fac886ef 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -18,114 +18,150 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Common.Extensions;
namespace Emby.Server.Implementations.Devices
{
public class DeviceManager : IDeviceManager
{
- private readonly IDeviceRepository _repo;
+ private readonly IJsonSerializer _json;
private readonly IUserManager _userManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly INetworkManager _network;
+ private readonly ILibraryManager _libraryManager;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IAuthenticationRepository _authRepo;
+
+ public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
- /// <summary>
- /// Occurs when [device options updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
+ private readonly object _cameraUploadSyncLock = new object();
+ private readonly object _capabilitiesSyncLock = new object();
- public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network)
+ public DeviceManager(IAuthenticationRepository authRepo, IJsonSerializer json, ILibraryManager libraryManager, ILocalizationManager localizationManager, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network)
{
- _repo = repo;
+ _json = json;
_userManager = userManager;
_fileSystem = fileSystem;
_libraryMonitor = libraryMonitor;
_config = config;
_logger = logger;
_network = network;
+ _libraryManager = libraryManager;
+ _localizationManager = localizationManager;
+ _authRepo = authRepo;
}
- public DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId)
+
+ private Dictionary<string, ClientCapabilities> _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
+ public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
{
- if (string.IsNullOrWhiteSpace(reportedId))
- {
- throw new ArgumentNullException("reportedId");
- }
+ var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
- var device = GetDevice(reportedId) ?? new DeviceInfo
+ lock (_capabilitiesSyncLock)
{
- Id = reportedId
- };
+ _capabilitiesCache[deviceId] = capabilities;
- device.ReportedName = name;
- device.AppName = appName;
- device.AppVersion = appVersion;
+ _json.SerializeToFile(capabilities, path);
+ }
+ }
- if (!string.IsNullOrWhiteSpace(usedByUserId))
- {
- var user = _userManager.GetUserById(usedByUserId);
+ public void UpdateDeviceOptions(string deviceId, DeviceOptions options)
+ {
+ _authRepo.UpdateDeviceOptions(deviceId, options);
- device.LastUserId = user.Id.ToString("N");
- device.LastUserName = user.Name;
+ if (DeviceOptionsUpdated != null)
+ {
+ DeviceOptionsUpdated(this, new GenericEventArgs<Tuple<string, DeviceOptions>>()
+ {
+ Argument = new Tuple<string, DeviceOptions>(deviceId, options)
+ });
}
+ }
- device.DateLastModified = DateTime.UtcNow;
+ public DeviceOptions GetDeviceOptions(string deviceId)
+ {
+ return _authRepo.GetDeviceOptions(deviceId);
+ }
- device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
+ public ClientCapabilities GetCapabilities(string id)
+ {
+ lock (_capabilitiesSyncLock)
+ {
+ ClientCapabilities result;
+ if (_capabilitiesCache.TryGetValue(id, out result))
+ {
+ return result;
+ }
- _repo.SaveDevice(device);
+ var path = Path.Combine(GetDevicePath(id), "capabilities.json");
+ try
+ {
+ return _json.DeserializeFromFile<ClientCapabilities>(path) ?? new ClientCapabilities();
+ }
+ catch
+ {
+ }
+ }
- return device;
+ return new ClientCapabilities();
}
- public void SaveCapabilities(string reportedId, ClientCapabilities capabilities)
+ public DeviceInfo GetDevice(string id)
{
- _repo.SaveCapabilities(reportedId, capabilities);
+ return GetDevice(id, true);
}
- public ClientCapabilities GetCapabilities(string reportedId)
+ private DeviceInfo GetDevice(string id, bool includeCapabilities)
{
- return _repo.GetCapabilities(reportedId);
- }
+ var session = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ DeviceId = id
- public DeviceInfo GetDevice(string id)
- {
- return _repo.GetDevice(id);
+ }).Items.FirstOrDefault();
+
+ var device = session == null ? null : ToDeviceInfo(session);
+
+ return device;
}
public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
{
- IEnumerable<DeviceInfo> devices = _repo.GetDevices();
+ var sessions = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ //UserId = query.UserId
+ HasUser = true
+
+ }).Items;
if (query.SupportsSync.HasValue)
{
var val = query.SupportsSync.Value;
- devices = devices.Where(i => i.Capabilities.SupportsSync == val);
+ sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val).ToArray();
}
- if (query.SupportsPersistentIdentifier.HasValue)
+ if (!query.UserId.Equals(Guid.Empty))
{
- var val = query.SupportsPersistentIdentifier.Value;
+ var user = _userManager.GetUserById(query.UserId);
- devices = devices.Where(i =>
- {
- var deviceVal = i.Capabilities.SupportsPersistentIdentifier;
- return deviceVal == val;
- });
+ sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)).ToArray();
}
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id));
- }
+ var array = sessions.Select(ToDeviceInfo).ToArray();
- var array = devices.ToArray();
return new QueryResult<DeviceInfo>
{
Items = array,
@@ -133,20 +169,59 @@ namespace Emby.Server.Implementations.Devices
};
}
- public void DeleteDevice(string id)
+ private DeviceInfo ToDeviceInfo(AuthenticationInfo authInfo)
+ {
+ var caps = GetCapabilities(authInfo.DeviceId);
+
+ return new DeviceInfo
+ {
+ AppName = authInfo.AppName,
+ AppVersion = authInfo.AppVersion,
+ Id = authInfo.DeviceId,
+ LastUserId = authInfo.UserId,
+ LastUserName = authInfo.UserName,
+ Name = authInfo.DeviceName,
+ DateLastActivity = authInfo.DateLastActivity,
+ IconUrl = caps == null ? null : caps.IconUrl
+ };
+ }
+
+ private string GetDevicesPath()
+ {
+ return Path.Combine(_config.ApplicationPaths.DataPath, "devices");
+ }
+
+ private string GetDevicePath(string id)
{
- _repo.DeleteDevice(id);
+ return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N"));
}
public ContentUploadHistory GetCameraUploadHistory(string deviceId)
{
- return _repo.GetCameraUploadHistory(deviceId);
+ var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
+
+ lock (_cameraUploadSyncLock)
+ {
+ try
+ {
+ return _json.DeserializeFromFile<ContentUploadHistory>(path);
+ }
+ catch (IOException)
+ {
+ return new ContentUploadHistory
+ {
+ DeviceId = deviceId
+ };
+ }
+ }
}
public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file)
{
- var device = GetDevice(deviceId);
- var path = GetUploadPath(device);
+ var device = GetDevice(deviceId, false);
+ var uploadPathInfo = GetUploadPath(device);
+
+ var path = uploadPathInfo.Item1;
if (!string.IsNullOrWhiteSpace(file.Album))
{
@@ -156,10 +231,12 @@ namespace Emby.Server.Implementations.Devices
path = Path.Combine(path, file.Name);
path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg");
- _libraryMonitor.ReportFileSystemChangeBeginning(path);
-
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+ await EnsureLibraryFolder(uploadPathInfo.Item2, uploadPathInfo.Item3).ConfigureAwait(false);
+
+ _libraryMonitor.ReportFileSystemChangeBeginning(path);
+
try
{
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
@@ -167,7 +244,7 @@ namespace Emby.Server.Implementations.Devices
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
- _repo.AddCameraUpload(deviceId, file);
+ AddCameraUpload(deviceId, file);
}
finally
{
@@ -187,65 +264,118 @@ namespace Emby.Server.Implementations.Devices
}
}
- private string GetUploadPath(DeviceInfo device)
+ private void AddCameraUpload(string deviceId, LocalFileInfo file)
{
- if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
+ var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+
+ lock (_cameraUploadSyncLock)
{
- return device.CameraUploadPath;
+ ContentUploadHistory history;
+
+ try
+ {
+ history = _json.DeserializeFromFile<ContentUploadHistory>(path);
+ }
+ catch (IOException)
+ {
+ history = new ContentUploadHistory
+ {
+ DeviceId = deviceId
+ };
+ }
+
+ history.DeviceId = deviceId;
+
+ var list = history.FilesUploaded.ToList();
+ list.Add(file);
+ history.FilesUploaded = list.ToArray(list.Count);
+
+ _json.SerializeToFile(history, path);
}
+ }
+ internal Task EnsureLibraryFolder(string path, string name)
+ {
+ var existingFolders = _libraryManager
+ .RootFolder
+ .Children
+ .OfType<Folder>()
+ .Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path))
+ .ToList();
+
+ if (existingFolders.Count > 0)
+ {
+ return Task.CompletedTask;
+ }
+
+ _fileSystem.CreateDirectory(path);
+
+ var libraryOptions = new LibraryOptions
+ {
+ PathInfos = new[] { new MediaPathInfo { Path = path } },
+ EnablePhotos = true,
+ EnableRealtimeMonitor = false,
+ SaveLocalMetadata = true
+ };
+
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ name = _localizationManager.GetLocalizedString("HeaderCameraUploads");
+ }
+
+ return _libraryManager.AddVirtualFolder(name, CollectionType.HomeVideos, libraryOptions, true);
+ }
+
+ private Tuple<string, string, string> GetUploadPath(DeviceInfo device)
+ {
var config = _config.GetUploadOptions();
var path = config.CameraUploadPath;
+
if (string.IsNullOrWhiteSpace(path))
{
path = DefaultCameraUploadsPath;
}
+ var topLibraryPath = path;
+
if (config.EnableCameraUploadSubfolders)
{
path = Path.Combine(path, _fileSystem.GetValidFilename(device.Name));
}
- return path;
- }
-
- private string DefaultCameraUploadsPath
- {
- get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
+ return new Tuple<string, string, string>(path, topLibraryPath, null);
}
- public void UpdateDeviceInfo(string id, DeviceOptions options)
+ internal string GetUploadsPath()
{
- var device = GetDevice(id);
-
- device.CustomName = options.CustomName;
- device.CameraUploadPath = options.CameraUploadPath;
+ var config = _config.GetUploadOptions();
+ var path = config.CameraUploadPath;
- device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ path = DefaultCameraUploadsPath;
+ }
- _repo.SaveDevice(device);
+ return path;
+ }
- EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger);
+ private string DefaultCameraUploadsPath
+ {
+ get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
}
- public bool CanAccessDevice(string userId, string deviceId)
+ public bool CanAccessDevice(User user, string deviceId)
{
- if (string.IsNullOrWhiteSpace(userId))
+ if (user == null)
{
- throw new ArgumentNullException("userId");
+ throw new ArgumentException("user not found");
}
- if (string.IsNullOrWhiteSpace(deviceId))
+ if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException("deviceId");
}
- var user = _userManager.GetUserById(userId);
-
- if (user == null)
- {
- throw new ArgumentException("user not found");
- }
-
if (!CanAccessDevice(user.Policy, deviceId))
{
var capabilities = GetCapabilities(deviceId);
@@ -271,15 +401,89 @@ namespace Emby.Server.Implementations.Devices
return true;
}
- return ListHelper.ContainsIgnoreCase(policy.EnabledDevices, id);
+ return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+
+ public class DeviceManagerEntryPoint : IServerEntryPoint
+ {
+ private readonly DeviceManager _deviceManager;
+ private readonly IServerConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
+ private ILogger _logger;
+
+ public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ {
+ _deviceManager = (DeviceManager)deviceManager;
+ _config = config;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ }
+
+ public async void Run()
+ {
+ if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
+ {
+ var path = _deviceManager.GetUploadsPath();
+
+ if (_fileSystem.DirectoryExists(path))
+ {
+ try
+ {
+ await _deviceManager.EnsureLibraryFolder(path, null).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error creating camera uploads library", ex);
+ }
+
+ _config.Configuration.CameraUploadUpgraded = true;
+ _config.SaveConfiguration();
+ }
+ }
+ }
+
+ #region IDisposable Support
+ private bool disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: dispose managed state (managed objects).
+ }
+
+ // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+ // TODO: set large fields to null.
+
+ disposedValue = true;
+ }
+ }
+
+ // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
+ // ~DeviceManagerEntryPoint() {
+ // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ // Dispose(false);
+ // }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
}
+ #endregion
}
public class DevicesConfigStore : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
- return new List<ConfigurationStore>
+ return new ConfigurationStore[]
{
new ConfigurationStore
{
diff --git a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
deleted file mode 100644
index d7817b17a..000000000
--- a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
+++ /dev/null
@@ -1,451 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using Emby.Server.Implementations.Data;
-using MediaBrowser.Controller;
-using MediaBrowser.Model.Logging;
-using SQLitePCL.pretty;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Controller.Configuration;
-
-namespace Emby.Server.Implementations.Devices
-{
- public class SqliteDeviceRepository : BaseSqliteRepository, IDeviceRepository
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- protected IFileSystem FileSystem { get; private set; }
- private readonly object _syncLock = new object();
- private readonly IJsonSerializer _json;
- private IServerApplicationPaths _appPaths;
-
- public SqliteDeviceRepository(ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IJsonSerializer json)
- : base(logger)
- {
- var appPaths = config.ApplicationPaths;
-
- DbFilePath = Path.Combine(appPaths.DataPath, "devices.db");
- FileSystem = fileSystem;
- _json = json;
- _appPaths = appPaths;
- }
-
- public void Initialize()
- {
- try
- {
- InitializeInternal();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
-
- FileSystem.DeleteFile(DbFilePath);
-
- InitializeInternal();
- }
- }
-
- private void InitializeInternal()
- {
- using (var connection = CreateConnection())
- {
- RunDefaultInitialization(connection);
-
- string[] queries = {
- "create table if not exists Devices (Id TEXT PRIMARY KEY, Name TEXT NOT NULL, ReportedName TEXT NOT NULL, CustomName TEXT, CameraUploadPath TEXT, LastUserName TEXT, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, LastUserId TEXT, DateLastModified DATETIME NOT NULL, Capabilities TEXT NOT NULL)",
- "create index if not exists idx_id on Devices(Id)"
- };
-
- connection.RunQueries(queries);
-
- MigrateDevices();
- }
- }
-
- private void MigrateDevices()
- {
- List<string> files;
- try
- {
- files = FileSystem
- .GetFilePaths(GetDevicesPath(), true)
- .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
- catch (IOException)
- {
- return;
- }
-
- foreach (var file in files)
- {
- try
- {
- var device = _json.DeserializeFromFile<DeviceInfo>(file);
-
- device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
-
- SaveDevice(device);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error reading {0}", ex, file);
- }
- finally
- {
- try
- {
- FileSystem.DeleteFile(file);
- }
- catch (IOException)
- {
- try
- {
- FileSystem.MoveFile(file, Path.ChangeExtension(file, ".old"));
- }
- catch (IOException)
- {
- }
- }
- }
- }
- }
-
- private const string BaseSelectText = "select Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities from Devices";
-
- public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
- {
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update devices set Capabilities=@Capabilities where Id=@Id"))
- {
- statement.TryBind("@Id", deviceId);
-
- if (capabilities == null)
- {
- statement.TryBindNull("@Capabilities");
- }
- else
- {
- statement.TryBind("@Capabilities", _json.SerializeToString(capabilities));
- }
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- public void SaveDevice(DeviceInfo entry)
- {
- if (entry == null)
- {
- throw new ArgumentNullException("entry");
- }
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("replace into Devices (Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities) values (@Id, @Name, @ReportedName, @CustomName, @CameraUploadPath, @LastUserName, @AppName, @AppVersion, @LastUserId, @DateLastModified, @Capabilities)"))
- {
- statement.TryBind("@Id", entry.Id);
- statement.TryBind("@Name", entry.Name);
- statement.TryBind("@ReportedName", entry.ReportedName);
- statement.TryBind("@CustomName", entry.CustomName);
- statement.TryBind("@CameraUploadPath", entry.CameraUploadPath);
- statement.TryBind("@LastUserName", entry.LastUserName);
- statement.TryBind("@AppName", entry.AppName);
- statement.TryBind("@AppVersion", entry.AppVersion);
- statement.TryBind("@DateLastModified", entry.DateLastModified);
-
- if (entry.Capabilities == null)
- {
- statement.TryBindNull("@Capabilities");
- }
- else
- {
- statement.TryBind("@Capabilities", _json.SerializeToString(entry.Capabilities));
- }
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- public DeviceInfo GetDevice(string id)
- {
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var statementTexts = new List<string>();
- statementTexts.Add(BaseSelectText + " where Id=@Id");
-
- return connection.RunInTransaction(db =>
- {
- var statements = PrepareAllSafe(db, statementTexts).ToList();
-
- using (var statement = statements[0])
- {
- statement.TryBind("@Id", id);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetEntry(row);
- }
- }
-
- return null;
-
- }, ReadTransactionMode);
- }
- }
- }
-
- public List<DeviceInfo> GetDevices()
- {
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var statementTexts = new List<string>();
- statementTexts.Add(BaseSelectText + " order by DateLastModified desc");
-
- return connection.RunInTransaction(db =>
- {
- var list = new List<DeviceInfo>();
-
- var statements = PrepareAllSafe(db, statementTexts).ToList();
-
- using (var statement = statements[0])
- {
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(GetEntry(row));
- }
- }
-
- return list;
-
- }, ReadTransactionMode);
- }
- }
- }
-
- public ClientCapabilities GetCapabilities(string id)
- {
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var statementTexts = new List<string>();
- statementTexts.Add("Select Capabilities from Devices where Id=@Id");
-
- return connection.RunInTransaction(db =>
- {
- var statements = PrepareAllSafe(db, statementTexts).ToList();
-
- using (var statement = statements[0])
- {
- statement.TryBind("@Id", id);
-
- foreach (var row in statement.ExecuteQuery())
- {
- if (row[0].SQLiteType != SQLiteType.Null)
- {
- return _json.DeserializeFromString<ClientCapabilities>(row.GetString(0));
- }
- }
- }
-
- return null;
-
- }, ReadTransactionMode);
- }
- }
- }
-
- private DeviceInfo GetEntry(IReadOnlyList<IResultSetValue> reader)
- {
- var index = 0;
-
- var info = new DeviceInfo
- {
- Id = reader.GetString(index)
- };
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Name = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.ReportedName = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.CustomName = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.CameraUploadPath = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.LastUserName = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.AppName = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.AppVersion = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.LastUserId = reader.GetString(index);
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.DateLastModified = reader[index].ReadDateTime();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Capabilities = _json.DeserializeFromString<ClientCapabilities>(reader.GetString(index));
- }
-
- return info;
- }
-
- private string GetDevicesPath()
- {
- return Path.Combine(_appPaths.DataPath, "devices");
- }
-
- private string GetDevicePath(string id)
- {
- return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N"));
- }
-
- public ContentUploadHistory GetCameraUploadHistory(string deviceId)
- {
- var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
-
- lock (_syncLock)
- {
- try
- {
- return _json.DeserializeFromFile<ContentUploadHistory>(path);
- }
- catch (IOException)
- {
- return new ContentUploadHistory
- {
- DeviceId = deviceId
- };
- }
- }
- }
-
- public void AddCameraUpload(string deviceId, LocalFileInfo file)
- {
- var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
-
- lock (_syncLock)
- {
- ContentUploadHistory history;
-
- try
- {
- history = _json.DeserializeFromFile<ContentUploadHistory>(path);
- }
- catch (IOException)
- {
- history = new ContentUploadHistory
- {
- DeviceId = deviceId
- };
- }
-
- history.DeviceId = deviceId;
-
- var list = history.FilesUploaded.ToList();
- list.Add(file);
- history.FilesUploaded = list.ToArray(list.Count);
-
- _json.SerializeToFile(history, path);
- }
- }
-
- public void DeleteDevice(string id)
- {
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("delete from devices where Id=@Id"))
- {
- statement.TryBind("@Id", id);
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
-
- var path = GetDevicePath(id);
-
- lock (_syncLock)
- {
- try
- {
- FileSystem.DeleteDirectory(path, true);
- }
- catch (IOException)
- {
- }
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
index a0a5f32ef..a709607bd 100644
--- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
+++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Model.Diagnostics;
+using System.Threading;
namespace Emby.Server.Implementations.Diagnostics
{
@@ -48,8 +49,32 @@ namespace Emby.Server.Implementations.Diagnostics
}
}
+ private bool _hasExited;
+ private bool HasExited
+ {
+ get
+ {
+ if (_hasExited)
+ {
+ return true;
+ }
+
+ try
+ {
+ _hasExited = _process.HasExited;
+ }
+ catch (InvalidOperationException)
+ {
+ _hasExited = true;
+ }
+
+ return _hasExited;
+ }
+ }
+
private void _process_Exited(object sender, EventArgs e)
{
+ _hasExited = true;
if (Exited != null)
{
Exited(this, e);
@@ -98,13 +123,33 @@ namespace Emby.Server.Implementations.Diagnostics
public Task<bool> WaitForExitAsync(int timeMs)
{
- return Task.FromResult(_process.WaitForExit(timeMs));
+ //if (_process.WaitForExit(100))
+ //{
+ // return Task.FromResult(true);
+ //}
+
+ //timeMs -= 100;
+ timeMs = Math.Max(0, timeMs);
+
+ var tcs = new TaskCompletionSource<bool>();
+
+ var cancellationToken = new CancellationTokenSource(timeMs).Token;
+
+ if (HasExited)
+ {
+ return Task.FromResult(true);
+ }
+
+ _process.Exited += (sender, args) => tcs.TrySetResult(true);
+
+ cancellationToken.Register(() => tcs.TrySetResult(HasExited));
+
+ return tcs.Task;
}
public void Dispose()
{
_process.Dispose();
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 0a316fcf1..437917c45 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -18,16 +18,14 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Playlists;
namespace Emby.Server.Implementations.Dto
{
@@ -44,13 +42,12 @@ namespace Emby.Server.Implementations.Dto
private readonly IProviderManager _providerManager;
private readonly Func<IChannelManager> _channelManagerFactory;
- private readonly ISyncManager _syncManager;
private readonly IApplicationHost _appHost;
private readonly Func<IDeviceManager> _deviceManager;
private readonly Func<IMediaSourceManager> _mediaSourceManager;
private readonly Func<ILiveTvManager> _livetvManager;
- public DtoService(ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory, ISyncManager syncManager, IApplicationHost appHost, Func<IDeviceManager> deviceManager, Func<IMediaSourceManager> mediaSourceManager, Func<ILiveTvManager> livetvManager)
+ public DtoService(ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory, IApplicationHost appHost, Func<IDeviceManager> deviceManager, Func<IMediaSourceManager> mediaSourceManager, Func<ILiveTvManager> livetvManager)
{
_logger = logger;
_libraryManager = libraryManager;
@@ -61,7 +58,6 @@ namespace Emby.Server.Implementations.Dto
_fileSystem = fileSystem;
_providerManager = providerManager;
_channelManagerFactory = channelManagerFactory;
- _syncManager = syncManager;
_appHost = appHost;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
@@ -99,18 +95,6 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto[] GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null)
{
- if (items == null)
- {
- throw new ArgumentNullException("items");
- }
-
- if (options == null)
- {
- throw new ArgumentNullException("options");
- }
-
- var syncDictionary = GetSyncedItemProgress(options);
-
var returnItems = new BaseItemDto[itemCount];
var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
@@ -136,7 +120,7 @@ namespace Emby.Server.Implementations.Dto
if (byName != null)
{
- if (options.Fields.Contains(ItemFields.ItemCounts))
+ if (options.ContainsField(ItemFields.ItemCounts))
{
var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user)
{
@@ -147,12 +131,10 @@ namespace Emby.Server.Implementations.Dto
}
});
- SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
+ SetItemByNameInfo(item, dto, libraryItems, user);
}
}
- FillSyncInfo(dto, item, options, user, syncDictionary);
-
returnItems[index] = dto;
index++;
}
@@ -173,8 +155,6 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
{
- var syncDictionary = GetSyncedItemProgress(options);
-
var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user, owner);
var tvChannel = item as LiveTvChannel;
@@ -194,7 +174,7 @@ namespace Emby.Server.Implementations.Dto
if (byName != null)
{
- if (options.Fields.Contains(ItemFields.ItemCounts))
+ if (options.ContainsField(ItemFields.ItemCounts))
{
SetItemByNameInfo(item, dto, GetTaggedItems(byName, user, new DtoOptions(false)
{
@@ -203,123 +183,24 @@ namespace Emby.Server.Implementations.Dto
}), user);
}
- FillSyncInfo(dto, item, options, user, syncDictionary);
return dto;
}
- FillSyncInfo(dto, item, options, user, syncDictionary);
-
return dto;
}
- private List<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
+ private IList<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
{
- var items = byName.GetTaggedItems(new InternalItemsQuery(user)
+ return byName.GetTaggedItems(new InternalItemsQuery(user)
{
Recursive = true,
DtoOptions = options
- }).ToList();
-
- return items;
- }
-
- public Dictionary<string, SyncedItemProgress> GetSyncedItemProgress(DtoOptions options)
- {
- if (!options.Fields.Contains(ItemFields.BasicSyncInfo) &&
- !options.Fields.Contains(ItemFields.SyncInfo))
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- var deviceId = options.DeviceId;
- if (string.IsNullOrWhiteSpace(deviceId))
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- var caps = _deviceManager().GetCapabilities(deviceId);
- if (caps == null || !caps.SupportsSync)
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- return _syncManager.GetSyncedItemProgresses(new SyncJobItemQuery
- {
- TargetId = deviceId,
- Statuses = new[]
- {
- SyncJobItemStatus.Converting,
- SyncJobItemStatus.Queued,
- SyncJobItemStatus.Transferring,
- SyncJobItemStatus.ReadyToTransfer,
- SyncJobItemStatus.Synced
- }
});
}
- public void FillSyncInfo(IEnumerable<Tuple<BaseItem, BaseItemDto>> tuples, DtoOptions options, User user)
- {
- if (options.Fields.Contains(ItemFields.BasicSyncInfo) ||
- options.Fields.Contains(ItemFields.SyncInfo))
- {
- var syncProgress = GetSyncedItemProgress(options);
-
- foreach (var tuple in tuples)
- {
- var item = tuple.Item1;
-
- FillSyncInfo(tuple.Item2, item, options, user, syncProgress);
- }
- }
- }
-
- private void FillSyncInfo(IHasSyncInfo dto, BaseItem item, DtoOptions options, User user, Dictionary<string, SyncedItemProgress> syncProgress)
- {
- var hasFullSyncInfo = options.Fields.Contains(ItemFields.SyncInfo);
-
- if (!hasFullSyncInfo && !options.Fields.Contains(ItemFields.BasicSyncInfo))
- {
- return;
- }
-
- if (dto.SupportsSync ?? false)
- {
- SyncedItemProgress syncStatus;
- if (syncProgress.TryGetValue(dto.Id, out syncStatus))
- {
- if (syncStatus.Status == SyncJobItemStatus.Synced)
- {
- dto.SyncPercent = 100;
- }
- else
- {
- dto.SyncPercent = syncStatus.Progress;
- }
-
- if (hasFullSyncInfo)
- {
- dto.HasSyncJob = true;
- dto.SyncStatus = syncStatus.Status;
- }
- }
- }
- }
-
private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, List<Folder> allCollectionFolders, User user = null, BaseItem owner = null)
{
- var fields = options.Fields;
-
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- if (fields == null)
- {
- throw new ArgumentNullException("fields");
- }
-
var dto = new BaseItemDto
{
ServerId = _appHost.SystemId
@@ -330,12 +211,12 @@ namespace Emby.Server.Implementations.Dto
dto.SourceType = item.SourceType.ToString();
}
- if (fields.Contains(ItemFields.People))
+ if (options.ContainsField(ItemFields.People))
{
AttachPeople(dto, item);
}
- if (fields.Contains(ItemFields.PrimaryImageAspectRatio))
+ if (options.ContainsField(ItemFields.PrimaryImageAspectRatio))
{
try
{
@@ -348,7 +229,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (fields.Contains(ItemFields.DisplayPreferencesId))
+ if (options.ContainsField(ItemFields.DisplayPreferencesId))
{
dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N");
}
@@ -361,74 +242,54 @@ namespace Emby.Server.Implementations.Dto
var hasMediaSources = item as IHasMediaSources;
if (hasMediaSources != null)
{
- if (fields.Contains(ItemFields.MediaSources))
+ if (options.ContainsField(ItemFields.MediaSources))
{
- if (user == null)
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true);
- }
- else
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user);
- }
+ dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
NormalizeMediaSourceContainers(dto);
}
}
- if (fields.Contains(ItemFields.Studios))
+ if (options.ContainsField(ItemFields.Studios))
{
AttachStudios(dto, item);
}
AttachBasicFields(dto, item, owner, options);
- var collectionFolder = item as ICollectionFolder;
- if (collectionFolder != null)
- {
- dto.CollectionType = collectionFolder.CollectionType;
- }
-
- if (fields.Contains(ItemFields.CanDelete))
+ if (options.ContainsField(ItemFields.CanDelete))
{
dto.CanDelete = user == null
? item.CanDelete()
: item.CanDelete(user);
}
- if (fields.Contains(ItemFields.CanDownload))
+ if (options.ContainsField(ItemFields.CanDownload))
{
dto.CanDownload = user == null
? item.CanDownload()
: item.CanDownload(user);
}
- if (fields.Contains(ItemFields.Etag))
+ if (options.ContainsField(ItemFields.Etag))
{
dto.Etag = item.GetEtag(user);
}
var liveTvManager = _livetvManager();
- if (item is ILiveTvRecording)
- {
- liveTvManager.AddInfoToRecordingDto(item, dto, user);
- }
- else
+ var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
+ if (activeRecording != null)
{
- var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
- if (activeRecording != null)
- {
- dto.Type = "Recording";
- dto.CanDownload = false;
- dto.RunTimeTicks = null;
+ dto.Type = "Recording";
+ dto.CanDownload = false;
+ dto.RunTimeTicks = null;
- if (!string.IsNullOrWhiteSpace(dto.SeriesName))
- {
- dto.EpisodeTitle = dto.Name;
- dto.Name = dto.SeriesName;
- }
- liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
+ if (!string.IsNullOrEmpty(dto.SeriesName))
+ {
+ dto.EpisodeTitle = dto.Name;
+ dto.Name = dto.SeriesName;
}
+ liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
}
return dto;
@@ -439,7 +300,7 @@ namespace Emby.Server.Implementations.Dto
foreach (var mediaSource in dto.MediaSources)
{
var container = mediaSource.Container;
- if (string.IsNullOrWhiteSpace(container))
+ if (string.IsNullOrEmpty(container))
{
continue;
}
@@ -452,17 +313,17 @@ namespace Emby.Server.Implementations.Dto
var path = mediaSource.Path;
string fileExtensionContainer = null;
- if (!string.IsNullOrWhiteSpace(path))
+ if (!string.IsNullOrEmpty(path))
{
path = Path.GetExtension(path);
- if (!string.IsNullOrWhiteSpace(path))
+ if (!string.IsNullOrEmpty(path))
{
path = Path.GetExtension(path);
- if (!string.IsNullOrWhiteSpace(path))
+ if (!string.IsNullOrEmpty(path))
{
path = path.TrimStart('.');
}
- if (!string.IsNullOrWhiteSpace(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase))
{
fileExtensionContainer = path;
}
@@ -473,22 +334,20 @@ namespace Emby.Server.Implementations.Dto
}
}
- public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
+ public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
{
var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user);
- if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
+ if (taggedItems != null && options.ContainsField(ItemFields.ItemCounts))
{
SetItemByNameInfo(item, dto, taggedItems, user);
}
- FillSyncInfo(dto, item, options, user, syncProgress);
-
return dto;
}
- private void SetItemByNameInfo(BaseItem item, BaseItemDto dto, List<BaseItem> taggedItems, User user = null)
+ private void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems, User user = null)
{
if (item is MusicArtist)
{
@@ -529,39 +388,37 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
- private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions)
+ private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions options)
{
- var fields = dtoOptions.Fields;
-
if (item.IsFolder)
{
var folder = (Folder)item;
- if (dtoOptions.EnableUserData)
+ if (options.EnableUserData)
{
- dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, dtoOptions.Fields);
+ dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, options);
}
if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
{
// For these types we can try to optimize and assume these values will be equal
- if (item is MusicAlbum || item is Season)
+ if (item is MusicAlbum || item is Season || item is Playlist)
{
dto.ChildCount = dto.RecursiveItemCount;
}
- if (dtoOptions.Fields.Contains(ItemFields.ChildCount))
+ if (options.ContainsField(ItemFields.ChildCount))
{
dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
}
}
- if (fields.Contains(ItemFields.CumulativeRunTimeTicks))
+ if (options.ContainsField(ItemFields.CumulativeRunTimeTicks))
{
dto.CumulativeRunTimeTicks = item.RunTimeTicks;
}
- if (fields.Contains(ItemFields.DateLastMediaAdded))
+ if (options.ContainsField(ItemFields.DateLastMediaAdded))
{
dto.DateLastMediaAdded = folder.DateLastMediaAdded;
}
@@ -569,21 +426,21 @@ namespace Emby.Server.Implementations.Dto
else
{
- if (dtoOptions.EnableUserData)
+ if (options.EnableUserData)
{
dto.UserData = _userDataRepository.GetUserDataDto(item, user);
}
}
- if (/*!(item is LiveTvProgram) ||*/ fields.Contains(ItemFields.PlayAccess))
+ if (options.ContainsField(ItemFields.PlayAccess))
{
dto.PlayAccess = item.GetPlayAccess(user);
}
- if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo))
+ if (options.ContainsField(ItemFields.BasicSyncInfo))
{
var userCanSync = user != null && user.Policy.EnableContentDownloading;
- if (userCanSync && _syncManager.SupportsSync(item))
+ if (userCanSync && item.SupportsExternalTransfer)
{
dto.SupportsSync = true;
}
@@ -610,47 +467,15 @@ namespace Emby.Server.Implementations.Dto
/// <exception cref="System.ArgumentNullException">item</exception>
public string GetDtoId(BaseItem item)
{
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
return item.Id.ToString("N");
}
- /// <summary>
- /// Converts a UserItemData to a DTOUserItemData
- /// </summary>
- /// <param name="data">The data.</param>
- /// <returns>DtoUserItemData.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public UserItemDataDto GetUserItemDataDto(UserItemData data)
- {
- if (data == null)
- {
- throw new ArgumentNullException("data");
- }
-
- return new UserItemDataDto
- {
- IsFavorite = data.IsFavorite,
- Likes = data.Likes,
- PlaybackPositionTicks = data.PlaybackPositionTicks,
- PlayCount = data.PlayCount,
- Rating = data.Rating,
- Played = data.Played,
- LastPlayedDate = data.LastPlayedDate,
- Key = data.Key
- };
- }
private void SetBookProperties(BaseItemDto dto, Book item)
{
dto.SeriesName = item.SeriesName;
}
private void SetPhotoProperties(BaseItemDto dto, Photo item)
{
- dto.Width = item.Width;
- dto.Height = item.Height;
dto.CameraMake = item.CameraMake;
dto.CameraModel = item.CameraModel;
dto.Software = item.Software;
@@ -670,7 +495,7 @@ namespace Emby.Server.Implementations.Dto
if (album != null)
{
dto.Album = album.Name;
- dto.AlbumId = album.Id.ToString("N");
+ dto.AlbumId = album.Id;
}
}
@@ -688,7 +513,7 @@ namespace Emby.Server.Implementations.Dto
if (parentAlbumIds.Count > 0)
{
- dto.AlbumId = parentAlbumIds[0].ToString("N");
+ dto.AlbumId = parentAlbumIds[0];
}
}
@@ -835,11 +660,11 @@ namespace Emby.Server.Implementations.Dto
private void AttachStudios(BaseItemDto dto, BaseItem item)
{
dto.Studios = item.Studios
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => new NameIdPair
+ .Where(i => !string.IsNullOrEmpty(i))
+ .Select(i => new NameGuidPair
{
Name = i,
- Id = _libraryManager.GetStudioId(i).ToString("N")
+ Id = _libraryManager.GetStudioId(i)
})
.ToArray();
}
@@ -847,8 +672,8 @@ namespace Emby.Server.Implementations.Dto
private void AttachGenreItems(BaseItemDto dto, BaseItem item)
{
dto.GenreItems = item.Genres
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => new NameIdPair
+ .Where(i => !string.IsNullOrEmpty(i))
+ .Select(i => new NameGuidPair
{
Name = i,
Id = GetGenreId(i, item)
@@ -856,53 +681,19 @@ namespace Emby.Server.Implementations.Dto
.ToArray();
}
- private string GetGenreId(string name, BaseItem owner)
+ private Guid GetGenreId(string name, BaseItem owner)
{
if (owner is IHasMusicGenres)
{
- return _libraryManager.GetMusicGenreId(name).ToString("N");
+ return _libraryManager.GetMusicGenreId(name);
}
if (owner is Game || owner is GameSystem)
{
- return _libraryManager.GetGameGenreId(name).ToString("N");
+ return _libraryManager.GetGameGenreId(name);
}
- return _libraryManager.GetGenreId(name).ToString("N");
- }
-
- /// <summary>
- /// Gets the chapter info dto.
- /// </summary>
- /// <param name="chapterInfo">The chapter info.</param>
- /// <param name="item">The item.</param>
- /// <returns>ChapterInfoDto.</returns>
- private ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item)
- {
- var dto = new ChapterInfoDto
- {
- Name = chapterInfo.Name,
- StartPositionTicks = chapterInfo.StartPositionTicks
- };
-
- if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
- {
- dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
- {
- Path = chapterInfo.ImagePath,
- Type = ImageType.Chapter,
- DateModified = chapterInfo.ImageDateModified
- });
- }
-
- return dto;
- }
-
- public List<ChapterInfoDto> GetChapterInfoDtos(BaseItem item)
- {
- return _itemRepo.GetChapters(item.Id)
- .Select(c => GetChapterInfoDto(c, item))
- .ToList();
+ return _libraryManager.GetGenreId(name);
}
/// <summary>
@@ -914,14 +705,12 @@ namespace Emby.Server.Implementations.Dto
/// <param name="options">The options.</param>
private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, DtoOptions options)
{
- var fields = options.Fields;
-
- if (fields.Contains(ItemFields.DateCreated))
+ if (options.ContainsField(ItemFields.DateCreated))
{
dto.DateCreated = item.DateCreated;
}
- if (fields.Contains(ItemFields.Settings))
+ if (options.ContainsField(ItemFields.Settings))
{
dto.LockedFields = item.LockedFields;
dto.LockData = item.IsLocked;
@@ -931,17 +720,12 @@ namespace Emby.Server.Implementations.Dto
dto.EndDate = item.EndDate;
- if (fields.Contains(ItemFields.HomePageUrl))
- {
- dto.HomePageUrl = item.HomePageUrl;
- }
-
- if (fields.Contains(ItemFields.ExternalUrls))
+ if (options.ContainsField(ItemFields.ExternalUrls))
{
dto.ExternalUrls = _providerManager.GetExternalUrls(item).ToArray();
}
- if (fields.Contains(ItemFields.Tags))
+ if (options.ContainsField(ItemFields.Tags))
{
dto.Tags = item.Tags;
}
@@ -958,7 +742,7 @@ namespace Emby.Server.Implementations.Dto
dto.BackdropImageTags = GetImageTags(item, item.GetImages(ImageType.Backdrop).Take(backdropLimit).ToList());
}
- if (fields.Contains(ItemFields.ScreenshotImageTags))
+ if (options.ContainsField(ItemFields.ScreenshotImageTags))
{
var screenshotLimit = options.GetImageLimit(ImageType.Screenshot);
if (screenshotLimit > 0)
@@ -967,7 +751,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (fields.Contains(ItemFields.Genres))
+ if (options.ContainsField(ItemFields.Genres))
{
dto.Genres = item.Genres;
AttachGenreItems(dto, item);
@@ -994,7 +778,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- dto.Id = GetDtoId(item);
+ dto.Id = item.Id;
dto.IndexNumber = item.IndexNumber;
dto.ParentIndexNumber = item.ParentIndexNumber;
@@ -1014,13 +798,9 @@ namespace Emby.Server.Implementations.Dto
dto.LocationType = item.LocationType;
}
- if (item.IsHD.HasValue && item.IsHD.Value)
- {
- dto.IsHD = item.IsHD;
- }
dto.Audio = item.Audio;
- if (fields.Contains(ItemFields.Settings))
+ if (options.ContainsField(ItemFields.Settings))
{
dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode;
dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage;
@@ -1028,90 +808,83 @@ namespace Emby.Server.Implementations.Dto
dto.CriticRating = item.CriticRating;
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- dto.LocalTrailerCount = hasTrailers.GetTrailerIds().Count;
- }
-
var hasDisplayOrder = item as IHasDisplayOrder;
if (hasDisplayOrder != null)
{
dto.DisplayOrder = hasDisplayOrder.DisplayOrder;
}
- var userView = item as UserView;
- if (userView != null)
+ var hasCollectionType = item as IHasCollectionType;
+ if (hasCollectionType != null)
{
- dto.CollectionType = userView.ViewType;
+ dto.CollectionType = hasCollectionType.CollectionType;
}
- if (fields.Contains(ItemFields.RemoteTrailers))
+ if (options.ContainsField(ItemFields.RemoteTrailers))
{
- dto.RemoteTrailers = hasTrailers != null ?
- hasTrailers.RemoteTrailers :
- new MediaUrl[] { };
+ dto.RemoteTrailers = item.RemoteTrailers;
}
dto.Name = item.Name;
dto.OfficialRating = item.OfficialRating;
- if (fields.Contains(ItemFields.Overview))
+ if (options.ContainsField(ItemFields.Overview))
{
dto.Overview = item.Overview;
}
- if (fields.Contains(ItemFields.OriginalTitle))
+ if (options.ContainsField(ItemFields.OriginalTitle))
{
dto.OriginalTitle = item.OriginalTitle;
}
- if (fields.Contains(ItemFields.ParentId))
+ if (options.ContainsField(ItemFields.ParentId))
{
- var displayParentId = item.DisplayParentId;
- if (displayParentId.HasValue)
- {
- dto.ParentId = displayParentId.Value.ToString("N");
- }
+ dto.ParentId = item.DisplayParentId;
}
AddInheritedImages(dto, item, options, owner);
- if (fields.Contains(ItemFields.Path))
+ if (options.ContainsField(ItemFields.Path))
{
dto.Path = GetMappedPath(item, owner);
}
+ if (options.ContainsField(ItemFields.EnableMediaSourceDisplay))
+ {
+ dto.EnableMediaSourceDisplay = item.EnableMediaSourceDisplay;
+ }
+
dto.PremiereDate = item.PremiereDate;
dto.ProductionYear = item.ProductionYear;
- if (fields.Contains(ItemFields.ProviderIds))
+ if (options.ContainsField(ItemFields.ProviderIds))
{
dto.ProviderIds = item.ProviderIds;
}
dto.RunTimeTicks = item.RunTimeTicks;
- if (fields.Contains(ItemFields.SortName))
+ if (options.ContainsField(ItemFields.SortName))
{
dto.SortName = item.SortName;
}
- if (fields.Contains(ItemFields.CustomRating))
+ if (options.ContainsField(ItemFields.CustomRating))
{
dto.CustomRating = item.CustomRating;
}
- if (fields.Contains(ItemFields.Taglines))
+ if (options.ContainsField(ItemFields.Taglines))
{
- if (!string.IsNullOrWhiteSpace(item.Tagline))
+ if (!string.IsNullOrEmpty(item.Tagline))
{
dto.Taglines = new string[] { item.Tagline };
}
if (dto.Taglines == null)
{
- dto.Taglines = new string[] { };
+ dto.Taglines = Array.Empty<string>();
}
}
@@ -1141,12 +914,12 @@ namespace Emby.Server.Implementations.Dto
if (albumParent != null)
{
- dto.AlbumId = GetDtoId(albumParent);
+ dto.AlbumId = albumParent.Id;
dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
}
- //if (fields.Contains(ItemFields.MediaSourceCount))
+ //if (options.ContainsField(ItemFields.MediaSourceCount))
//{
// Songs always have one
//}
@@ -1182,7 +955,7 @@ namespace Emby.Server.Implementations.Dto
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
- if (string.IsNullOrWhiteSpace(i))
+ if (string.IsNullOrEmpty(i))
{
return null;
}
@@ -1193,10 +966,10 @@ namespace Emby.Server.Implementations.Dto
});
if (artist != null)
{
- return new NameIdPair
+ return new NameGuidPair
{
Name = artist.Name,
- Id = artist.Id.ToString("N")
+ Id = artist.Id
};
}
@@ -1233,7 +1006,7 @@ namespace Emby.Server.Implementations.Dto
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
- if (string.IsNullOrWhiteSpace(i))
+ if (string.IsNullOrEmpty(i))
{
return null;
}
@@ -1244,10 +1017,10 @@ namespace Emby.Server.Implementations.Dto
});
if (artist != null)
{
- return new NameIdPair
+ return new NameGuidPair
{
Name = artist.Name,
- Id = artist.Id.ToString("N")
+ Id = artist.Id
};
}
@@ -1274,7 +1047,7 @@ namespace Emby.Server.Implementations.Dto
dto.PartCount = video.AdditionalParts.Length + 1;
}
- if (fields.Contains(ItemFields.MediaSourceCount))
+ if (options.ContainsField(ItemFields.MediaSourceCount))
{
var mediaSourceCount = video.MediaSourceCount;
if (mediaSourceCount != 1)
@@ -1283,9 +1056,9 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (fields.Contains(ItemFields.Chapters))
+ if (options.ContainsField(ItemFields.Chapters))
{
- dto.Chapters = GetChapterInfoDtos(item);
+ dto.Chapters = _itemRepo.GetChapters(item);
}
if (video.ExtraType.HasValue)
@@ -1294,7 +1067,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (fields.Contains(ItemFields.MediaStreams))
+ if (options.ContainsField(ItemFields.MediaStreams))
{
// Add VideoInfo
var iHasMediaSources = item as IHasMediaSources;
@@ -1303,30 +1076,48 @@ namespace Emby.Server.Implementations.Dto
{
MediaStream[] mediaStreams;
- if (dto.MediaSources != null && dto.MediaSources.Count > 0)
+ if (dto.MediaSources != null && dto.MediaSources.Length > 0)
{
- mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id)
- .SelectMany(i => i.MediaStreams)
- .ToArray();
+ if (item.SourceType == SourceType.Channel)
+ {
+ mediaStreams = dto.MediaSources[0].MediaStreams.ToArray();
+ }
+ else
+ {
+ mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, item.Id.ToString("N"), StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.MediaStreams)
+ .ToArray();
+ }
}
else
{
- mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams.ToArray();
+ mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true).First().MediaStreams.ToArray();
}
dto.MediaStreams = mediaStreams;
}
}
- var hasSpecialFeatures = item as IHasSpecialFeatures;
- if (hasSpecialFeatures != null)
+ BaseItem[] allExtras = null;
+
+ if (options.ContainsField(ItemFields.SpecialFeatureCount))
{
- var specialFeatureCount = hasSpecialFeatures.SpecialFeatureIds.Length;
+ if (allExtras == null)
+ {
+ allExtras = item.GetExtras().ToArray();
+ }
- if (specialFeatureCount > 0)
+ dto.SpecialFeatureCount = allExtras.Count(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value));
+ }
+
+ if (options.ContainsField(ItemFields.LocalTrailerCount))
+ {
+ if (allExtras == null)
{
- dto.SpecialFeatureCount = specialFeatureCount;
+ allExtras = item.GetExtras().ToArray();
}
+
+ dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer);
}
// Add EpisodeInfo
@@ -1336,37 +1127,20 @@ namespace Emby.Server.Implementations.Dto
dto.IndexNumberEnd = episode.IndexNumberEnd;
dto.SeriesName = episode.SeriesName;
- if (fields.Contains(ItemFields.AlternateEpisodeNumbers))
- {
- dto.DvdSeasonNumber = episode.DvdSeasonNumber;
- dto.DvdEpisodeNumber = episode.DvdEpisodeNumber;
- dto.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber;
- }
-
- if (fields.Contains(ItemFields.SpecialEpisodeNumbers))
+ if (options.ContainsField(ItemFields.SpecialEpisodeNumbers))
{
dto.AirsAfterSeasonNumber = episode.AirsAfterSeasonNumber;
dto.AirsBeforeEpisodeNumber = episode.AirsBeforeEpisodeNumber;
dto.AirsBeforeSeasonNumber = episode.AirsBeforeSeasonNumber;
}
- var seasonId = episode.SeasonId;
- if (seasonId.HasValue)
- {
- dto.SeasonId = seasonId.Value.ToString("N");
- }
-
dto.SeasonName = episode.SeasonName;
-
- var seriesId = episode.SeriesId;
- if (seriesId.HasValue)
- {
- dto.SeriesId = seriesId.Value.ToString("N");
- }
+ dto.SeasonId = episode.SeasonId;
+ dto.SeriesId = episode.SeriesId;
Series episodeSeries = null;
- //if (fields.Contains(ItemFields.SeriesPrimaryImage))
+ //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
episodeSeries = episodeSeries ?? episode.Series;
if (episodeSeries != null)
@@ -1375,7 +1149,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (fields.Contains(ItemFields.SeriesStudio))
+ if (options.ContainsField(ItemFields.SeriesStudio))
{
episodeSeries = episodeSeries ?? episode.Series;
if (episodeSeries != null)
@@ -1399,16 +1173,11 @@ namespace Emby.Server.Implementations.Dto
if (season != null)
{
dto.SeriesName = season.SeriesName;
-
- var seriesId = season.SeriesId;
- if (seriesId.HasValue)
- {
- dto.SeriesId = seriesId.Value.ToString("N");
- }
+ dto.SeriesId = season.SeriesId;
series = null;
- if (fields.Contains(ItemFields.SeriesStudio))
+ if (options.ContainsField(ItemFields.SeriesStudio))
{
series = series ?? season.Series;
if (series != null)
@@ -1417,7 +1186,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- //if (fields.Contains(ItemFields.SeriesPrimaryImage))
+ //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
series = series ?? season.Series;
if (series != null)
@@ -1453,7 +1222,7 @@ namespace Emby.Server.Implementations.Dto
SetBookProperties(dto, book);
}
- if (fields.Contains(ItemFields.ProductionLocations))
+ if (options.ContainsField(ItemFields.ProductionLocations))
{
if (item.ProductionLocations.Length > 0 || item is Movie)
{
@@ -1461,6 +1230,33 @@ namespace Emby.Server.Implementations.Dto
}
}
+ if (options.ContainsField(ItemFields.Width))
+ {
+ var width = item.Width;
+ if (width > 0)
+ {
+ dto.Width = width;
+ }
+ }
+
+ if (options.ContainsField(ItemFields.Height))
+ {
+ var height = item.Height;
+ if (height > 0)
+ {
+ dto.Height = height;
+ }
+ }
+
+ if (options.ContainsField(ItemFields.IsHD))
+ {
+ // Compatibility
+ if (item.IsHD)
+ {
+ dto.IsHD = true;
+ }
+ }
+
var photo = item as Photo;
if (photo != null)
{
@@ -1469,7 +1265,7 @@ namespace Emby.Server.Implementations.Dto
dto.ChannelId = item.ChannelId;
- if (item.SourceType == SourceType.Channel && !string.IsNullOrWhiteSpace(item.ChannelId))
+ if (item.SourceType == SourceType.Channel)
{
var channel = _libraryManager.GetItemById(item.ChannelId);
if (channel != null)
@@ -1491,7 +1287,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- var parent = currentItem.DisplayParent ?? (currentItem.IsOwnedItem ? currentItem.GetOwner() : currentItem.GetParent());
+ var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent();
if (parent == null && !(originalItem is UserRootFolder) && !(originalItem is UserView) && !(originalItem is AggregateFolder) && !(originalItem is ICollectionFolder) && !(originalItem is Channel))
{
@@ -1592,9 +1388,7 @@ namespace Emby.Server.Implementations.Dto
{
var path = item.Path;
- var locationType = item.LocationType;
-
- if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
+ if (item.IsFileProtocol)
{
path = _libraryManager.GetPathAfterNetworkSubstitution(path, ownerItem ?? item);
}
@@ -1628,15 +1422,15 @@ namespace Emby.Server.Implementations.Dto
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
- if (defaultAspectRatio.HasValue)
+ if (defaultAspectRatio > 0)
{
- if (supportedEnhancers.Count == 0)
+ if (supportedEnhancers.Length == 0)
{
- return defaultAspectRatio.Value;
+ return defaultAspectRatio;
}
double dummyWidth = 200;
- double dummyHeight = dummyWidth / defaultAspectRatio.Value;
+ double dummyHeight = dummyWidth / defaultAspectRatio;
size = new ImageSize(dummyWidth, dummyHeight);
}
else
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index cef37910e..66dd80dbe 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -1,679 +1,44 @@
-<?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')" />
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <ItemGroup>
+ <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
+ <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
+ <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
+ <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
+ <ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj" />
+ <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
+ <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
+ <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
+ <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj" />
+ <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
+ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Emby.XmlTv" Version="1.0.18" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.2.0" />
+ <PackageReference Include="sharpcompress" Version="0.22.0" />
+ <PackageReference Include="SimpleInjector" Version="4.3.0" />
+ <PackageReference Include="SQLitePCL.pretty.core" Version="1.1.8" />
+ <PackageReference Include="SQLitePCLRaw.core" Version="1.1.11" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="..\SharedVersion.cs" />
+ </ItemGroup>
+
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{E383961B-9356-4D5D-8233-9A1079D03055}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Emby.Server.Implementations</RootNamespace>
- <AssemblyName>Emby.Server.Implementations</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
- <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</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>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Activity\ActivityLogEntryPoint.cs" />
- <Compile Include="Activity\ActivityManager.cs" />
- <Compile Include="Activity\ActivityRepository.cs" />
- <Compile Include="AppBase\BaseApplicationPaths.cs" />
- <Compile Include="AppBase\BaseConfigurationManager.cs" />
- <Compile Include="AppBase\ConfigurationHelper.cs" />
- <Compile Include="ApplicationHost.cs" />
- <Compile Include="Archiving\ZipClient.cs" />
- <Compile Include="Branding\BrandingConfigurationFactory.cs" />
- <Compile Include="Browser\BrowserLauncher.cs" />
- <Compile Include="Channels\ChannelConfigurations.cs" />
- <Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" />
- <Compile Include="Channels\ChannelImageProvider.cs" />
- <Compile Include="Channels\ChannelManager.cs" />
- <Compile Include="Channels\ChannelPostScanTask.cs" />
- <Compile Include="Channels\RefreshChannelsScheduledTask.cs" />
- <Compile Include="Collections\CollectionImageProvider.cs" />
- <Compile Include="Collections\CollectionManager.cs" />
- <Compile Include="Collections\CollectionsDynamicFolder.cs" />
- <Compile Include="Configuration\ServerConfigurationManager.cs" />
- <Compile Include="Cryptography\CryptographyProvider.cs" />
- <Compile Include="Data\ManagedConnection.cs" />
- <Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
- <Compile Include="Data\SqliteItemRepository.cs" />
- <Compile Include="Data\SqliteUserDataRepository.cs" />
- <Compile Include="Data\SqliteUserRepository.cs" />
- <Compile Include="Data\TypeMapper.cs" />
- <Compile Include="Devices\CameraUploadsDynamicFolder.cs" />
- <Compile Include="Devices\CameraUploadsFolder.cs" />
- <Compile Include="Devices\DeviceId.cs" />
- <Compile Include="Devices\DeviceManager.cs" />
- <Compile Include="Devices\SqliteDeviceRepository.cs" />
- <Compile Include="Diagnostics\CommonProcess.cs" />
- <Compile Include="Diagnostics\ProcessFactory.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" />
- <Compile Include="EntryPoints\RecordingNotifier.cs" />
- <Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
- <Compile Include="EntryPoints\ServerEventNotifier.cs" />
- <Compile Include="EntryPoints\StartupWizard.cs" />
- <Compile Include="EntryPoints\SystemEvents.cs" />
- <Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
- <Compile Include="EntryPoints\UsageEntryPoint.cs" />
- <Compile Include="EntryPoints\UsageReporter.cs" />
- <Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
- <Compile Include="EnvironmentInfo\EnvironmentInfo.cs" />
- <Compile Include="FFMpeg\FFMpegInfo.cs" />
- <Compile Include="FFMpeg\FFMpegInstallInfo.cs" />
- <Compile Include="FFMpeg\FFMpegLoader.cs" />
- <Compile Include="HttpClientManager\HttpClientInfo.cs" />
- <Compile Include="HttpClientManager\HttpClientManager.cs" />
- <Compile Include="HttpServerFactory.cs" />
- <Compile Include="HttpServer\FileWriter.cs" />
- <Compile Include="HttpServer\HttpListenerHost.cs" />
- <Compile Include="HttpServer\HttpResultFactory.cs" />
- <Compile Include="HttpServer\LoggerUtils.cs" />
- <Compile Include="HttpServer\RangeRequestWriter.cs" />
- <Compile Include="HttpServer\ResponseFilter.cs" />
- <Compile Include="HttpServer\SocketSharp\Extensions.cs" />
- <Compile Include="HttpServer\SocketSharp\HttpUtility.cs" />
- <Compile Include="HttpServer\IHttpListener.cs" />
- <Compile Include="HttpServer\Security\AuthorizationContext.cs" />
- <Compile Include="HttpServer\Security\AuthService.cs" />
- <Compile Include="HttpServer\Security\SessionContext.cs" />
- <Compile Include="HttpServer\SocketSharp\RequestMono.cs" />
- <Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpListener.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
- <Compile Include="HttpServer\StreamWriter.cs" />
- <Compile Include="Images\BaseDynamicImageProvider.cs" />
- <Compile Include="IO\FileRefresher.cs" />
- <Compile Include="IO\IsoManager.cs" />
- <Compile Include="IO\LibraryMonitor.cs" />
- <Compile Include="IO\ManagedFileSystem.cs" />
- <Compile Include="IO\MbLinkShortcutHandler.cs" />
- <Compile Include="IO\MemoryStreamProvider.cs" />
- <Compile Include="IO\SharpCifsFileSystem.cs" />
- <Compile Include="IO\SharpCifs\Config.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcBind.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcBinding.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcConstants.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcError.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcException.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcMessage.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcPipeHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\DcerpcSecurityProvider.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\LsaPolicyHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\Lsarpc.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\LsarSidArrayX.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcDfsRootEnum.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcEnumerateAliasesInDomain.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcGetMembersInAlias.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcLookupSids.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcLsarOpenPolicy2.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcQueryInformationPolicy.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcSamrConnect2.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcSamrConnect4.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcSamrOpenAlias.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcSamrOpenDomain.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcShareEnum.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\MsrpcShareGetInfo.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\Netdfs.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\Samr.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\SamrAliasHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\SamrDomainHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\SamrPolicyHandle.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Msrpc\Srvsvc.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrBuffer.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrException.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrHyper.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrLong.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrObject.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrShort.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Ndr\NdrSmall.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\Rpc.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\UnicodeString.cs" />
- <Compile Include="IO\SharpCifs\Dcerpc\UUID.cs" />
- <Compile Include="IO\SharpCifs\Netbios\Lmhosts.cs" />
- <Compile Include="IO\SharpCifs\Netbios\Name.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NameQueryRequest.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NameQueryResponse.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NameServiceClient.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NameServicePacket.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NbtAddress.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NbtException.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NodeStatusRequest.cs" />
- <Compile Include="IO\SharpCifs\Netbios\NodeStatusResponse.cs" />
- <Compile Include="IO\SharpCifs\Netbios\SessionRequestPacket.cs" />
- <Compile Include="IO\SharpCifs\Netbios\SessionRetargetResponsePacket.cs" />
- <Compile Include="IO\SharpCifs\Netbios\SessionServicePacket.cs" />
- <Compile Include="IO\SharpCifs\Ntlmssp\NtlmFlags.cs" />
- <Compile Include="IO\SharpCifs\Ntlmssp\NtlmMessage.cs" />
- <Compile Include="IO\SharpCifs\Ntlmssp\Type1Message.cs" />
- <Compile Include="IO\SharpCifs\Ntlmssp\Type2Message.cs" />
- <Compile Include="IO\SharpCifs\Ntlmssp\Type3Message.cs" />
- <Compile Include="IO\SharpCifs\Smb\ACE.cs" />
- <Compile Include="IO\SharpCifs\Smb\AllocInfo.cs" />
- <Compile Include="IO\SharpCifs\Smb\AndXServerMessageBlock.cs" />
- <Compile Include="IO\SharpCifs\Smb\BufferCache.cs" />
- <Compile Include="IO\SharpCifs\Smb\Dfs.cs" />
- <Compile Include="IO\SharpCifs\Smb\DfsReferral.cs" />
- <Compile Include="IO\SharpCifs\Smb\DosError.cs" />
- <Compile Include="IO\SharpCifs\Smb\DosFileFilter.cs" />
- <Compile Include="IO\SharpCifs\Smb\FileEntry.cs" />
- <Compile Include="IO\SharpCifs\Smb\IInfo.cs" />
- <Compile Include="IO\SharpCifs\Smb\NetServerEnum2.cs" />
- <Compile Include="IO\SharpCifs\Smb\NetServerEnum2Response.cs" />
- <Compile Include="IO\SharpCifs\Smb\NetShareEnum.cs" />
- <Compile Include="IO\SharpCifs\Smb\NetShareEnumResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtlmAuthenticator.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtlmChallenge.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtlmContext.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtlmPasswordAuthentication.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtStatus.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtTransQuerySecurityDesc.cs" />
- <Compile Include="IO\SharpCifs\Smb\NtTransQuerySecurityDescResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\Principal.cs" />
- <Compile Include="IO\SharpCifs\Smb\SecurityDescriptor.cs" />
- <Compile Include="IO\SharpCifs\Smb\ServerMessageBlock.cs" />
- <Compile Include="IO\SharpCifs\Smb\SID.cs" />
- <Compile Include="IO\SharpCifs\Smb\SigningDigest.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbAuthException.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComBlankResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComClose.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComCreateDirectory.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComDelete.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComDeleteDirectory.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComFindClose2.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComLogoffAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNegotiate.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNegotiateResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNTCreateAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNTCreateAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNtTransaction.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComNtTransactionResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComOpenAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComOpenAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComQueryInformation.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComQueryInformationResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComReadAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComReadAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComRename.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComSessionSetupAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComSessionSetupAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComTransaction.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComTransactionResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComTreeConnectAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComTreeConnectAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComTreeDisconnect.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComWrite.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComWriteAndX.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComWriteAndXResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbComWriteResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbConstants.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbException.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFile.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFileExtensions.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFileFilter.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFileInputStream.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFilenameFilter.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbFileOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbNamedPipe.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbRandomAccessFile.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbSession.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbShareInfo.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbTransport.cs" />
- <Compile Include="IO\SharpCifs\Smb\SmbTree.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2FindFirst2.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2FindFirst2Response.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2FindNext2.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2GetDfsReferral.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2GetDfsReferralResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2QueryFSInformation.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2QueryFSInformationResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2QueryPathInformation.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2QueryPathInformationResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2SetFileInformation.cs" />
- <Compile Include="IO\SharpCifs\Smb\Trans2SetFileInformationResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransactNamedPipeInputStream.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransactNamedPipeOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransCallNamedPipe.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransCallNamedPipeResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransPeekNamedPipe.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransPeekNamedPipeResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransTransactNamedPipe.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransTransactNamedPipeResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransWaitNamedPipe.cs" />
- <Compile Include="IO\SharpCifs\Smb\TransWaitNamedPipeResponse.cs" />
- <Compile Include="IO\SharpCifs\Smb\WinError.cs" />
- <Compile Include="IO\SharpCifs\UniAddress.cs" />
- <Compile Include="IO\SharpCifs\Util\Base64.cs" />
- <Compile Include="IO\SharpCifs\Util\DES.cs" />
- <Compile Include="IO\SharpCifs\Util\Encdec.cs" />
- <Compile Include="IO\SharpCifs\Util\Hexdump.cs" />
- <Compile Include="IO\SharpCifs\Util\HMACT64.cs" />
- <Compile Include="IO\SharpCifs\Util\LogStream.cs" />
- <Compile Include="IO\SharpCifs\Util\MD4.cs" />
- <Compile Include="IO\SharpCifs\Util\RC4.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\AbstractMap.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Arrays.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\BufferedReader.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\BufferedWriter.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\CharBuffer.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\CharSequence.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Collections.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ConcurrentHashMap.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\DateFormat.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\EnumeratorWrapper.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Exceptions.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Extensions.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FileInputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FileOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FilePath.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FileReader.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FileWriter.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FilterInputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\FilterOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Hashtable.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\HttpURLConnection.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ICallable.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IConcurrentMap.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IExecutor.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IFilenameFilter.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IFuture.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\InputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\InputStreamReader.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IPrivilegedAction.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\IRunnable.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Iterator.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\LinkageError.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Matcher.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\MD5.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\MD5Managed.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\MessageDigest.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\NetworkStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ObjectInputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ObjectOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\OutputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\OutputStreamWriter.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\PipedInputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\PipedOutputStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\PrintWriter.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Properties.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\RandomAccessFile.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ReentrantLock.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Reference.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Runtime.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\SimpleDateFormat.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\SocketEx.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\StringTokenizer.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\SynchronizedList.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\Thread.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ThreadFactory.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\ThreadPoolExecutor.cs" />
- <Compile Include="IO\SharpCifs\Util\Sharpen\WrappedSystemStream.cs" />
- <Compile Include="IO\SharpCifs\Util\Transport\Request.cs" />
- <Compile Include="IO\SharpCifs\Util\Transport\Response.cs" />
- <Compile Include="IO\SharpCifs\Util\Transport\Transport.cs" />
- <Compile Include="IO\SharpCifs\Util\Transport\TransportException.cs" />
- <Compile Include="IO\ThrottledStream.cs" />
- <Compile Include="Library\CoreResolutionIgnoreRule.cs" />
- <Compile Include="Library\LibraryManager.cs" />
- <Compile Include="Library\LocalTrailerPostScanTask.cs" />
- <Compile Include="Library\MediaSourceManager.cs" />
- <Compile Include="Library\MusicManager.cs" />
- <Compile Include="Library\PathExtensions.cs" />
- <Compile Include="Library\ResolverHelper.cs" />
- <Compile Include="Library\Resolvers\Audio\AudioResolver.cs" />
- <Compile Include="Library\Resolvers\Audio\MusicAlbumResolver.cs" />
- <Compile Include="Library\Resolvers\Audio\MusicArtistResolver.cs" />
- <Compile Include="Library\Resolvers\BaseVideoResolver.cs" />
- <Compile Include="Library\Resolvers\Books\BookResolver.cs" />
- <Compile Include="Library\Resolvers\FolderResolver.cs" />
- <Compile Include="Library\Resolvers\ItemResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\BoxSetResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\MovieResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoAlbumResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoResolver.cs" />
- <Compile Include="Library\Resolvers\PlaylistResolver.cs" />
- <Compile Include="Library\Resolvers\SpecialFolderResolver.cs" />
- <Compile Include="Library\Resolvers\TV\EpisodeResolver.cs" />
- <Compile Include="Library\Resolvers\TV\SeasonResolver.cs" />
- <Compile Include="Library\Resolvers\TV\SeriesResolver.cs" />
- <Compile Include="Library\Resolvers\VideoResolver.cs" />
- <Compile Include="Library\SearchEngine.cs" />
- <Compile Include="Library\UserDataManager.cs" />
- <Compile Include="Library\UserManager.cs" />
- <Compile Include="Library\UserViewManager.cs" />
- <Compile Include="Library\Validators\ArtistsPostScanTask.cs" />
- <Compile Include="Library\Validators\ArtistsValidator.cs" />
- <Compile Include="Library\Validators\GameGenresPostScanTask.cs" />
- <Compile Include="Library\Validators\GameGenresValidator.cs" />
- <Compile Include="Library\Validators\GenresPostScanTask.cs" />
- <Compile Include="Library\Validators\GenresValidator.cs" />
- <Compile Include="Library\Validators\MusicGenresPostScanTask.cs" />
- <Compile Include="Library\Validators\MusicGenresValidator.cs" />
- <Compile Include="Library\Validators\PeopleValidator.cs" />
- <Compile Include="Library\Validators\StudiosPostScanTask.cs" />
- <Compile Include="Library\Validators\StudiosValidator.cs" />
- <Compile Include="LiveTv\ChannelImageProvider.cs" />
- <Compile Include="LiveTv\EmbyTV\DirectRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\EmbyTV.cs" />
- <Compile Include="LiveTv\EmbyTV\EmbyTVRegistration.cs" />
- <Compile Include="LiveTv\EmbyTV\EncodedRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\EntryPoint.cs" />
- <Compile Include="LiveTv\EmbyTV\IRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\ItemDataProvider.cs" />
- <Compile Include="LiveTv\EmbyTV\RecordingHelper.cs" />
- <Compile Include="LiveTv\EmbyTV\SeriesTimerManager.cs" />
- <Compile Include="LiveTv\EmbyTV\TimerManager.cs" />
- <Compile Include="LiveTv\Listings\SchedulesDirect.cs" />
- <Compile Include="LiveTv\Listings\XmlTvListingsProvider.cs" />
- <Compile Include="LiveTv\LiveStreamHelper.cs" />
- <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />
- <Compile Include="LiveTv\LiveTvDtoService.cs" />
- <Compile Include="LiveTv\LiveTvManager.cs" />
- <Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" />
- <Compile Include="LiveTv\RecordingImageProvider.cs" />
- <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
- <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunManager.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunUdpStream.cs" />
- <Compile Include="LiveTv\TunerHosts\LiveStream.cs" />
- <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
- <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
- <Compile Include="LiveTv\TunerHosts\SharedHttpStream.cs" />
- <Compile Include="Localization\LocalizationManager.cs" />
- <Compile Include="Localization\TextLocalizer.cs" />
- <Compile Include="Logging\ConsoleLogger.cs" />
- <Compile Include="Logging\SimpleLogManager.cs" />
- <Compile Include="Logging\UnhandledExceptionWriter.cs" />
- <Compile Include="MediaEncoder\EncodingManager.cs" />
- <Compile Include="Migrations\IVersionMigration.cs" />
- <Compile Include="Networking\NetworkManager.cs" />
- <Compile Include="Net\DisposableManagedObjectBase.cs" />
- <Compile Include="Net\NetAcceptSocket.cs" />
- <Compile Include="Net\SocketFactory.cs" />
- <Compile Include="Net\UdpSocket.cs" />
- <Compile Include="News\NewsEntryPoint.cs" />
- <Compile Include="News\NewsService.cs" />
- <Compile Include="Notifications\CoreNotificationTypes.cs" />
- <Compile Include="Notifications\IConfigurableNotificationService.cs" />
- <Compile Include="Notifications\InternalNotificationService.cs" />
- <Compile Include="Notifications\NotificationConfigurationFactory.cs" />
- <Compile Include="Notifications\NotificationManager.cs" />
- <Compile Include="Notifications\Notifications.cs" />
- <Compile Include="Notifications\SqliteNotificationsRepository.cs" />
- <Compile Include="Notifications\WebSocketNotifier.cs" />
- <Compile Include="Data\BaseSqliteRepository.cs" />
- <Compile Include="Data\CleanDatabaseScheduledTask.cs" />
- <Compile Include="Data\SqliteExtensions.cs" />
- <Compile Include="Playlists\ManualPlaylistsFolder.cs" />
- <Compile Include="Playlists\PlaylistImageProvider.cs" />
- <Compile Include="Playlists\PlaylistManager.cs" />
- <Compile Include="Playlists\PlaylistsDynamicFolder.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Reflection\AssemblyInfo.cs" />
- <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />
- <Compile Include="ScheduledTasks\DailyTrigger.cs" />
- <Compile Include="ScheduledTasks\IntervalTrigger.cs" />
- <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
- <Compile Include="ScheduledTasks\PluginUpdateTask.cs" />
- <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
- <Compile Include="ScheduledTasks\ScheduledTaskWorker.cs" />
- <Compile Include="ScheduledTasks\StartupTrigger.cs" />
- <Compile Include="ScheduledTasks\SystemEventTrigger.cs" />
- <Compile Include="ScheduledTasks\SystemUpdateTask.cs" />
- <Compile Include="ScheduledTasks\TaskManager.cs" />
- <Compile Include="ScheduledTasks\Tasks\DeleteCacheFileTask.cs" />
- <Compile Include="ScheduledTasks\Tasks\DeleteLogFileTask.cs" />
- <Compile Include="ScheduledTasks\Tasks\ReloadLoggerFileTask.cs" />
- <Compile Include="ScheduledTasks\WeeklyTrigger.cs" />
- <Compile Include="Security\AuthenticationRepository.cs" />
- <Compile Include="Security\EncryptionManager.cs" />
- <Compile Include="Security\MBLicenseFile.cs" />
- <Compile Include="Security\PluginSecurityManager.cs" />
- <Compile Include="Security\RegRecord.cs" />
- <Compile Include="Serialization\JsonSerializer.cs" />
- <Compile Include="Serialization\XmlSerializer.cs" />
- <Compile Include="ServerApplicationPaths.cs" />
- <Compile Include="ServerManager\ServerManager.cs" />
- <Compile Include="ServerManager\WebSocketConnection.cs" />
- <Compile Include="Services\ServicePath.cs" />
- <Compile Include="Services\ServiceMethod.cs" />
- <Compile Include="Services\ResponseHelper.cs" />
- <Compile Include="Services\HttpResult.cs" />
- <Compile Include="Services\RequestHelper.cs" />
- <Compile Include="Services\ServiceHandler.cs" />
- <Compile Include="Services\ServiceController.cs" />
- <Compile Include="Services\ServiceExec.cs" />
- <Compile Include="Services\StringMapTypeDeserializer.cs" />
- <Compile Include="Services\SwaggerService.cs" />
- <Compile Include="Services\UrlExtensions.cs" />
- <Compile Include="Session\HttpSessionController.cs" />
- <Compile Include="Session\SessionManager.cs" />
- <Compile Include="Session\SessionWebSocketListener.cs" />
- <Compile Include="Session\WebSocketController.cs" />
- <Compile Include="Social\SharingManager.cs" />
- <Compile Include="Social\SharingRepository.cs" />
- <Compile Include="Sorting\AiredEpisodeOrderComparer.cs" />
- <Compile Include="Sorting\AlbumArtistComparer.cs" />
- <Compile Include="Sorting\AlbumComparer.cs" />
- <Compile Include="Sorting\AlphanumComparator.cs" />
- <Compile Include="Sorting\ArtistComparer.cs" />
- <Compile Include="Sorting\CommunityRatingComparer.cs" />
- <Compile Include="Sorting\CriticRatingComparer.cs" />
- <Compile Include="Sorting\DateCreatedComparer.cs" />
- <Compile Include="Sorting\DateLastMediaAddedComparer.cs" />
- <Compile Include="Sorting\DatePlayedComparer.cs" />
- <Compile Include="Sorting\GameSystemComparer.cs" />
- <Compile Include="Sorting\IsFavoriteOrLikeComparer.cs" />
- <Compile Include="Sorting\IsFolderComparer.cs" />
- <Compile Include="Sorting\IsPlayedComparer.cs" />
- <Compile Include="Sorting\IsUnplayedComparer.cs" />
- <Compile Include="Sorting\NameComparer.cs" />
- <Compile Include="Sorting\OfficialRatingComparer.cs" />
- <Compile Include="Sorting\PlayCountComparer.cs" />
- <Compile Include="Sorting\PlayersComparer.cs" />
- <Compile Include="Sorting\PremiereDateComparer.cs" />
- <Compile Include="Sorting\ProductionYearComparer.cs" />
- <Compile Include="Sorting\RandomComparer.cs" />
- <Compile Include="Sorting\RuntimeComparer.cs" />
- <Compile Include="Sorting\SeriesSortNameComparer.cs" />
- <Compile Include="Sorting\SortNameComparer.cs" />
- <Compile Include="Sorting\StartDateComparer.cs" />
- <Compile Include="Sorting\StudioComparer.cs" />
- <Compile Include="StartupOptions.cs" />
- <Compile Include="SystemEvents.cs" />
- <Compile Include="TextEncoding\NLangDetect\Detector.cs" />
- <Compile Include="TextEncoding\NLangDetect\DetectorFactory.cs" />
- <Compile Include="TextEncoding\NLangDetect\ErrorCode.cs" />
- <Compile Include="TextEncoding\NLangDetect\Extensions\CharExtensions.cs" />
- <Compile Include="TextEncoding\NLangDetect\Extensions\RandomExtensions.cs" />
- <Compile Include="TextEncoding\NLangDetect\Extensions\StringExtensions.cs" />
- <Compile Include="TextEncoding\NLangDetect\Extensions\UnicodeBlock.cs" />
- <Compile Include="TextEncoding\NLangDetect\GenProfile.cs" />
- <Compile Include="TextEncoding\NLangDetect\InternalException.cs" />
- <Compile Include="TextEncoding\NLangDetect\Language.cs" />
- <Compile Include="TextEncoding\NLangDetect\LanguageDetector.cs" />
- <Compile Include="TextEncoding\NLangDetect\NLangDetectException.cs" />
- <Compile Include="TextEncoding\NLangDetect\ProbVector.cs" />
- <Compile Include="TextEncoding\NLangDetect\Utils\LangProfile.cs" />
- <Compile Include="TextEncoding\NLangDetect\Utils\Messages.cs" />
- <Compile Include="TextEncoding\NLangDetect\Utils\NGram.cs" />
- <Compile Include="TextEncoding\NLangDetect\Utils\TagExtractor.cs" />
- <Compile Include="TextEncoding\TextEncoding.cs" />
- <Compile Include="TextEncoding\TextEncodingDetect.cs" />
- <Compile Include="TextEncoding\UniversalDetector\CharsetDetector.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\Big5Prober.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\BitPackage.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\CharDistributionAnalyser.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\CharsetProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\Charsets.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\CodingStateMachine.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\EscCharsetProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\EscSM.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\EUCJPProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\EUCKRProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\EUCTWProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\GB18030Prober.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\HebrewProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\JapaneseContextAnalyser.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangBulgarianModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangCyrillicModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangGreekModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangHebrewModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangHungarianModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\LangThaiModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\Latin1Prober.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\MBCSGroupProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\MBCSSM.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\SBCharsetProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\SBCSGroupProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\SequenceModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\SJISProber.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\SMModel.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\UniversalDetector.cs" />
- <Compile Include="TextEncoding\UniversalDetector\Core\UTF8Prober.cs" />
- <Compile Include="TextEncoding\UniversalDetector\DetectionConfidence.cs" />
- <Compile Include="TextEncoding\UniversalDetector\ICharsetDetector.cs" />
- <Compile Include="Threading\CommonTimer.cs" />
- <Compile Include="Threading\TimerFactory.cs" />
- <Compile Include="TV\SeriesPostScanTask.cs" />
- <Compile Include="TV\TVSeriesManager.cs" />
- <Compile Include="Udp\UdpServer.cs" />
- <Compile Include="Updates\InstallationManager.cs" />
- <Compile Include="UserViews\CollectionFolderImageProvider.cs" />
- <Compile Include="UserViews\DynamicImageProvider.cs" />
- <Compile Include="UserViews\FolderImageProvider.cs" />
- <Compile Include="Xml\XmlReaderSettingsFactory.cs" />
- </ItemGroup>
- <ItemGroup>
+
+ <ItemGroup>
<EmbeddedResource Include="Localization\iso6392.txt" />
- </ItemGroup>
- <ItemGroup>
- <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>
- </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.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.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.Naming">
- <HintPath>..\ThirdParty\emby\Emby.Naming.dll</HintPath>
- </Reference>
- <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.10\lib\portable-net45+netstandard2.0+win8\Emby.XmlTv.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="SharpCompress, Version=0.18.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
- <HintPath>..\packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll</HintPath>
- </Reference>
- <Reference Include="SimpleInjector, Version=4.0.12.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.4.0.12\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>
- </Reference>
- </ItemGroup>
- <ItemGroup>
- <Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <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" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Net.Http" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
<EmbeddedResource Include="Localization\countries.json" />
<EmbeddedResource Include="Localization\Core\ar.json" />
<EmbeddedResource Include="Localization\Core\bg-BG.json" />
@@ -710,128 +75,113 @@
<EmbeddedResource Include="Localization\Core\en-US.json" />
<EmbeddedResource Include="Localization\Core\el.json" />
<EmbeddedResource Include="Localization\Core\gsw.json" />
- <None Include="packages.config" />
- <None Include="TextEncoding\NLangDetect\Profiles\afr" />
- <None Include="TextEncoding\NLangDetect\Profiles\ara" />
- <None Include="TextEncoding\NLangDetect\Profiles\ben" />
- <None Include="TextEncoding\NLangDetect\Profiles\bul" />
- <None Include="TextEncoding\NLangDetect\Profiles\ces" />
- <None Include="TextEncoding\NLangDetect\Profiles\dan" />
- <None Include="TextEncoding\NLangDetect\Profiles\deu" />
- <None Include="TextEncoding\NLangDetect\Profiles\ell" />
- <None Include="TextEncoding\NLangDetect\Profiles\eng" />
- <None Include="TextEncoding\NLangDetect\Profiles\est" />
- <None Include="TextEncoding\NLangDetect\Profiles\fas" />
- <None Include="TextEncoding\NLangDetect\Profiles\fin" />
- <None Include="TextEncoding\NLangDetect\Profiles\fra" />
- <None Include="TextEncoding\NLangDetect\Profiles\guj" />
- <None Include="TextEncoding\NLangDetect\Profiles\heb" />
- <None Include="TextEncoding\NLangDetect\Profiles\hin" />
- <None Include="TextEncoding\NLangDetect\Profiles\hrv" />
- <None Include="TextEncoding\NLangDetect\Profiles\hun" />
- <None Include="TextEncoding\NLangDetect\Profiles\ind" />
- <None Include="TextEncoding\NLangDetect\Profiles\ita" />
- <None Include="TextEncoding\NLangDetect\Profiles\jpn" />
- <None Include="TextEncoding\NLangDetect\Profiles\kan" />
- <None Include="TextEncoding\NLangDetect\Profiles\kor" />
- <None Include="TextEncoding\NLangDetect\Profiles\lav" />
- <None Include="TextEncoding\NLangDetect\Profiles\lit" />
- <None Include="TextEncoding\NLangDetect\Profiles\mal" />
- <None Include="TextEncoding\NLangDetect\Profiles\mar" />
- <None Include="TextEncoding\NLangDetect\Profiles\mkd" />
- <None Include="TextEncoding\NLangDetect\Profiles\nep" />
- <None Include="TextEncoding\NLangDetect\Profiles\nld" />
- <None Include="TextEncoding\NLangDetect\Profiles\nor" />
- <None Include="TextEncoding\NLangDetect\Profiles\pan" />
- <None Include="TextEncoding\NLangDetect\Profiles\pol" />
- <None Include="TextEncoding\NLangDetect\Profiles\por" />
- <None Include="TextEncoding\NLangDetect\Profiles\ron" />
- <None Include="TextEncoding\NLangDetect\Profiles\rus" />
- <None Include="TextEncoding\NLangDetect\Profiles\slk" />
- <None Include="TextEncoding\NLangDetect\Profiles\slv" />
- <None Include="TextEncoding\NLangDetect\Profiles\som" />
- <None Include="TextEncoding\NLangDetect\Profiles\spa" />
- <None Include="TextEncoding\NLangDetect\Profiles\sqi" />
- <None Include="TextEncoding\NLangDetect\Profiles\swa" />
- <None Include="TextEncoding\NLangDetect\Profiles\swe" />
- <None Include="TextEncoding\NLangDetect\Profiles\tam" />
- <None Include="TextEncoding\NLangDetect\Profiles\tel" />
- <None Include="TextEncoding\NLangDetect\Profiles\tgl" />
- <None Include="TextEncoding\NLangDetect\Profiles\tha" />
- <None Include="TextEncoding\NLangDetect\Profiles\tur" />
- <None Include="TextEncoding\NLangDetect\Profiles\ukr" />
- <None Include="TextEncoding\NLangDetect\Profiles\urd" />
- <None Include="TextEncoding\NLangDetect\Profiles\vie" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\afr" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ara" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ben" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\bul" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ces" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\dan" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\deu" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ell" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\eng" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\est" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\fas" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\fin" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\fra" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\guj" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\heb" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\hin" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\hrv" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\hun" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ind" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ita" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\jpn" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\kan" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\kor" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\lav" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\lit" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\mal" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\mar" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\mkd" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\nep" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\nld" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\nor" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\pan" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\pol" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\por" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ron" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\rus" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\slk" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\slv" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\som" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\spa" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\sqi" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\swa" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\swe" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\tam" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\tel" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\tgl" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\tha" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\tur" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\ukr" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\urd" />
+ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\vie" />
<EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\zh-cn" />
<EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\zh-tw" />
<EmbeddedResource Include="TextEncoding\NLangDetect\Utils\messages.properties" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\au.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\be.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\br.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\ca.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\co.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\de.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\dk.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\fr.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\gb.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\ie.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\jp.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\kz.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\mx.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\nl.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\nz.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\ru.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\us.txt" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\uk.txt" />
- </ItemGroup>
- <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.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
-</Project> \ No newline at end of file
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\br.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\ca.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\co.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\dk.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\fr.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\gb.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\ie.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\jp.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\kz.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\mx.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\nl.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\nz.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\us.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\uk.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\es.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\ro.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="Emby.Server.MediaEncoding">
+ <HintPath>..\ThirdParty\emby\Emby.Server.MediaEncoding.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+
+</Project>
diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
index c2cee00c8..561f5ee12 100644
--- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
@@ -112,7 +112,6 @@ namespace Emby.Server.Implementations.EntryPoints
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
DisposeTimer();
- GC.SuppressFinalize(this);
}
private void DisposeTimer()
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 903bb0ff4..6801b2823 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -26,9 +26,10 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IDeviceDiscovery _deviceDiscovery;
private ITimer _timer;
- private bool _isStarted;
private readonly ITimerFactory _timerFactory;
+ private NatManager _natManager;
+
public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory)
{
_logger = logmanager.GetLogger("PortMapper");
@@ -37,6 +38,12 @@ namespace Emby.Server.Implementations.EntryPoints
_deviceDiscovery = deviceDiscovery;
_httpClient = httpClient;
_timerFactory = timerFactory;
+ _config.ConfigurationUpdated += _config_ConfigurationUpdated1;
+ }
+
+ private void _config_ConfigurationUpdated1(object sender, EventArgs e)
+ {
+ _config_ConfigurationUpdated(sender, e);
}
private string _lastConfigIdentifier;
@@ -49,8 +56,8 @@ namespace Emby.Server.Implementations.EntryPoints
values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
- values.Add((config.EnableHttps || config.RequireHttps).ToString());
values.Add(_appHost.EnableHttps.ToString());
+ values.Add((config.EnableRemoteAccess).ToString());
return string.Join("|", values.ToArray(values.Count));
}
@@ -59,10 +66,7 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
{
- if (_isStarted)
- {
- DisposeNat();
- }
+ DisposeNat();
Run();
}
@@ -70,10 +74,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Run()
{
- NatUtility.Logger = _logger;
- NatUtility.HttpClient = _httpClient;
-
- if (_config.Configuration.EnableUPnP)
+ if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess)
{
Start();
}
@@ -85,26 +86,18 @@ namespace Emby.Server.Implementations.EntryPoints
private void Start()
{
_logger.Debug("Starting NAT discovery");
- NatUtility.EnabledProtocols = new List<NatProtocol>
+ if (_natManager == null)
{
- NatProtocol.Pmp
- };
- NatUtility.DeviceFound += NatUtility_DeviceFound;
-
- // Mono.Nat does never rise this event. The event is there however it is useless.
- // You could remove it with no risk.
- NatUtility.DeviceLost += NatUtility_DeviceLost;
-
-
- NatUtility.StartDiscovery();
+ _natManager = new NatManager(_logger, _httpClient);
+ _natManager.DeviceFound += NatUtility_DeviceFound;
+ _natManager.StartDiscovery();
+ }
_timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
_lastConfigIdentifier = GetConfigIdentifier();
-
- _isStarted = true;
}
private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
@@ -182,8 +175,17 @@ namespace Emby.Server.Implementations.EntryPoints
return;
}
- _logger.Debug("Calling Nat.Handle on " + identifier);
- NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp);
+ // This should never happen, but the Handle method will throw ArgumentNullException if it does
+ if (localAddress == null)
+ {
+ return;
+ }
+
+ var natManager = _natManager;
+ if (natManager != null)
+ {
+ await natManager.Handle(localAddress, info, endpoint, NatProtocol.Upnp).ConfigureAwait(false);
+ }
}
}
@@ -209,19 +211,11 @@ namespace Emby.Server.Implementations.EntryPoints
try
{
var device = e.Device;
- _logger.Debug("NAT device found: {0}", device.LocalAddress.ToString());
CreateRules(device);
}
catch
{
- // I think it could be a good idea to log the exception because
- // you are using permanent portmapping here (never expire) and that means that next time
- // CreatePortMap is invoked it can fails with a 718-ConflictInMappingEntry or not. That depends
- // on the router's upnp implementation (specs says it should fail however some routers don't do it)
- // It also can fail with others like 727-ExternalPortOnlySupportsWildcard, 728-NoPortMapsAvailable
- // and those errors (upnp errors) could be useful for diagnosting.
-
// Commenting out because users are reporting problems out of our control
//_logger.ErrorException("Error creating port forwarding rules", ex);
}
@@ -238,14 +232,15 @@ namespace Emby.Server.Implementations.EntryPoints
// On some systems the device discovered event seems to fire repeatedly
// This check will help ensure we're not trying to port map the same device over and over
+ var address = device.LocalAddress;
- var address = device.LocalAddress.ToString();
+ var addressString = address.ToString();
lock (_createdRules)
{
- if (!_createdRules.Contains(address))
+ if (!_createdRules.Contains(addressString))
{
- _createdRules.Add(address);
+ _createdRules.Add(addressString);
}
else
{
@@ -253,41 +248,32 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
-
- if (success)
+ try
{
- await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
+ await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ return;
}
- }
-
- private async Task<bool> CreatePortMap(INatDevice device, int privatePort, int publicPort)
- {
- _logger.Debug("Creating port map on port {0}", privatePort);
try
{
- await device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
- {
- Description = _appHost.Name
-
- }).ConfigureAwait(false);
-
- return true;
+ await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
}
catch (Exception ex)
{
- _logger.Error("Error creating port map: " + ex.Message);
-
- return false;
}
}
- // As I said before, this method will be never invoked. You can remove it.
- void NatUtility_DeviceLost(object sender, DeviceEventArgs e)
+ private Task CreatePortMap(INatDevice device, int privatePort, int publicPort)
{
- var device = e.Device;
- _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
+ _logger.Debug("Creating port map on local port {0} to public port {1} with device {2}", privatePort, publicPort, device.LocalAddress.ToString());
+
+ return device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
+ {
+ Description = _appHost.Name
+ });
}
private bool _disposed = false;
@@ -295,7 +281,6 @@ namespace Emby.Server.Implementations.EntryPoints
{
_disposed = true;
DisposeNat();
- GC.SuppressFinalize(this);
}
private void DisposeNat()
@@ -310,27 +295,24 @@ namespace Emby.Server.Implementations.EntryPoints
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
- try
- {
- // This is not a significant improvement
- NatUtility.StopDiscovery();
- NatUtility.DeviceFound -= NatUtility_DeviceFound;
- NatUtility.DeviceLost -= NatUtility_DeviceLost;
- }
- // Statements in try-block will no fail because StopDiscovery is a one-line
- // method that was no chances to fail.
- // public static void StopDiscovery ()
- // {
- // searching.Reset();
- // }
- // IMO you could remove the catch-block
- catch (Exception ex)
- {
- _logger.ErrorException("Error stopping NAT Discovery", ex);
- }
- finally
+ var natManager = _natManager;
+
+ if (natManager != null)
{
- _isStarted = false;
+ _natManager = null;
+
+ using (natManager)
+ {
+ try
+ {
+ natManager.StopDiscovery();
+ natManager.DeviceFound -= NatUtility_DeviceFound;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error stopping NAT Discovery", ex);
+ }
+ }
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
index 221580681..8ae85e390 100644
--- a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
+++ b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
@@ -60,7 +60,6 @@ namespace Emby.Server.Implementations.EntryPoints
_timer.Dispose();
_timer = null;
}
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 0e771cbec..9a2ae34bc 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// The library update duration
/// </summary>
- private const int LibraryUpdateDuration = 5000;
+ private const int LibraryUpdateDuration = 30000;
private readonly IProviderManager _providerManager;
@@ -315,41 +315,39 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="cancellationToken">The cancellation token.</param>
private async void SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
{
- foreach (var user in _userManager.Users.ToList())
+ var userIds = _sessionManager.Sessions
+ .Select(i => i.UserId)
+ .Where(i => !i.Equals(Guid.Empty))
+ .Distinct()
+ .ToArray();
+
+ foreach (var userId in userIds)
{
- var id = user.Id;
- var userSessions = _sessionManager.Sessions
- .Where(u => u.UserId.HasValue && u.UserId.Value == id && u.SessionController != null && u.IsActive)
- .ToList();
+ LibraryUpdateInfo info;
- if (userSessions.Count > 0)
+ try
{
- LibraryUpdateInfo info;
-
- try
- {
- info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo,
- foldersRemovedFrom, id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in GetLibraryUpdateInfo", ex);
- return;
- }
-
- foreach (var userSession in userSessions)
- {
- try
- {
- await userSession.SessionController.SendLibraryUpdateInfo(info, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending LibraryChanged message", ex);
- }
- }
+ info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, userId);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in GetLibraryUpdateInfo", ex);
+ return;
}
+ if (info.IsEmpty)
+ {
+ continue;
+ }
+
+ try
+ {
+ await _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, "LibraryChanged", info, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending LibraryChanged message", ex);
+ }
}
}
@@ -391,7 +389,7 @@ namespace Emby.Server.Implementations.EntryPoints
private bool FilterItem(BaseItem item)
{
- if (!item.IsFolder && item.LocationType == LocationType.Virtual)
+ if (!item.IsFolder && !item.HasPathProtocol)
{
return false;
}
@@ -440,7 +438,7 @@ namespace Emby.Server.Implementations.EntryPoints
// If the physical root changed, return the user root
if (item is AggregateFolder)
{
- return new[] { user.RootFolder as T };
+ return new[] { _libraryManager.GetUserRootFolder() as T };
}
// Return it only if it's in the user's library
@@ -458,7 +456,6 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -474,10 +471,14 @@ namespace Emby.Server.Implementations.EntryPoints
LibraryUpdateTimer.Dispose();
LibraryUpdateTimer = null;
}
-
+
_libraryManager.ItemAdded -= libraryManager_ItemAdded;
_libraryManager.ItemUpdated -= libraryManager_ItemUpdated;
_libraryManager.ItemRemoved -= libraryManager_ItemRemoved;
+
+ _providerManager.RefreshCompleted -= _providerManager_RefreshCompleted;
+ _providerManager.RefreshStarted -= _providerManager_RefreshStarted;
+ _providerManager.RefreshProgress -= _providerManager_RefreshProgress;
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs b/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
deleted file mode 100644
index 21e075cf5..000000000
--- a/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Threading;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class LoadRegistrations
- /// </summary>
- public class LoadRegistrations : IServerEntryPoint
- {
- /// <summary>
- /// The _security manager
- /// </summary>
- private readonly ISecurityManager _securityManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private ITimer _timer;
- private readonly ITimerFactory _timerFactory;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LoadRegistrations" /> class.
- /// </summary>
- /// <param name="securityManager">The security manager.</param>
- /// <param name="logManager">The log manager.</param>
- public LoadRegistrations(ISecurityManager securityManager, ILogManager logManager, ITimerFactory timerFactory)
- {
- _securityManager = securityManager;
- _timerFactory = timerFactory;
-
- _logger = logManager.GetLogger("Registration Loader");
- }
-
- /// <summary>
- /// Runs this instance.
- /// </summary>
- public void Run()
- {
- _timer = _timerFactory.Create(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
- }
-
- private async Task LoadAllRegistrations()
- {
- try
- {
- await _securityManager.LoadAllRegistrationInfo().ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error loading registration info", ex);
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index f73b40b46..d41d76c6b 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -54,12 +54,16 @@ namespace Emby.Server.Implementations.EntryPoints
private async void SendMessage(string name, TimerEventInfo info)
{
- var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
+ var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id).ToList();
try
{
await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
}
+ catch (ObjectDisposedException)
+ {
+
+ }
catch (Exception ex)
{
_logger.ErrorException("Error sending message", ex);
@@ -72,7 +76,6 @@ namespace Emby.Server.Implementations.EntryPoints
_liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled;
_liveTvManager.TimerCreated -= _liveTvManager_TimerCreated;
_liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index 514321e20..e5748989e 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -22,11 +22,6 @@ namespace Emby.Server.Implementations.EntryPoints
public class ServerEventNotifier : IServerEntryPoint
{
/// <summary>
- /// The _server manager
- /// </summary>
- private readonly IServerManager _serverManager;
-
- /// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
@@ -47,23 +42,21 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ITaskManager _taskManager;
private readonly ISessionManager _sessionManager;
- private readonly ISyncManager _syncManager;
- public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, ISessionManager sessionManager, ISyncManager syncManager)
+ public ServerEventNotifier(IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, ISessionManager sessionManager)
{
- _serverManager = serverManager;
_userManager = userManager;
_installationManager = installationManager;
_appHost = appHost;
_taskManager = taskManager;
_sessionManager = sessionManager;
- _syncManager = syncManager;
}
public void Run()
{
_userManager.UserDeleted += userManager_UserDeleted;
_userManager.UserUpdated += userManager_UserUpdated;
+ _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
_userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
_appHost.HasPendingRestartChanged += kernel_HasPendingRestartChanged;
@@ -75,43 +68,31 @@ namespace Emby.Server.Implementations.EntryPoints
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
- _syncManager.SyncJobCreated += _syncManager_SyncJobCreated;
- _syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
- }
-
- void _syncManager_SyncJobCancelled(object sender, GenericEventArgs<SyncJob> e)
- {
- _sessionManager.SendMessageToUserDeviceSessions(e.Argument.TargetId, "SyncJobCancelled", e.Argument, CancellationToken.None);
- }
-
- void _syncManager_SyncJobCreated(object sender, GenericEventArgs<SyncJobCreationResult> e)
- {
- _sessionManager.SendMessageToUserDeviceSessions(e.Argument.Job.TargetId, "SyncJobCreated", e.Argument, CancellationToken.None);
}
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
{
- _serverManager.SendWebSocketMessage("PackageInstalling", e.InstallationInfo);
+ SendMessageToAdminSessions("PackageInstalling", e.InstallationInfo);
}
void _installationManager_PackageInstallationCancelled(object sender, InstallationEventArgs e)
{
- _serverManager.SendWebSocketMessage("PackageInstallationCancelled", e.InstallationInfo);
+ SendMessageToAdminSessions("PackageInstallationCancelled", e.InstallationInfo);
}
void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e)
{
- _serverManager.SendWebSocketMessage("PackageInstallationCompleted", e.InstallationInfo);
+ SendMessageToAdminSessions("PackageInstallationCompleted", e.InstallationInfo);
}
void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
- _serverManager.SendWebSocketMessage("PackageInstallationFailed", e.InstallationInfo);
+ SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo);
}
void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
{
- _serverManager.SendWebSocketMessage("ScheduledTaskEnded", e.Result);
+ SendMessageToAdminSessions("ScheduledTaskEnded", e.Result);
}
/// <summary>
@@ -121,7 +102,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="e">The e.</param>
void InstallationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
{
- _serverManager.SendWebSocketMessage("PluginUninstalled", e.Argument.GetPluginInfo());
+ SendMessageToAdminSessions("PluginUninstalled", e.Argument.GetPluginInfo());
}
/// <summary>
@@ -156,6 +137,13 @@ namespace Emby.Server.Implementations.EntryPoints
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N"));
}
+ void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
+ {
+ var dto = _userManager.GetUserDto(e.Argument);
+
+ SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto);
+ }
+
void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
{
var dto = _userManager.GetUserDto(e.Argument);
@@ -163,9 +151,36 @@ namespace Emby.Server.Implementations.EntryPoints
SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
}
+ private async void SendMessageToAdminSessions<T>(string name, T data)
+ {
+ try
+ {
+ await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None);
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+ catch (Exception)
+ {
+ //Logger.ErrorException("Error sending message", ex);
+ }
+ }
+
private async void SendMessageToUserSession<T>(User user, string name, T data)
{
- await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
+ try
+ {
+ await _sessionManager.SendMessageToUserSessions(new List<Guid> { user.Id }, name, data, CancellationToken.None);
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+ catch (Exception)
+ {
+ //Logger.ErrorException("Error sending message", ex);
+ }
}
/// <summary>
@@ -174,7 +189,6 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -187,6 +201,7 @@ namespace Emby.Server.Implementations.EntryPoints
{
_userManager.UserDeleted -= userManager_UserDeleted;
_userManager.UserUpdated -= userManager_UserUpdated;
+ _userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated;
_userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
_installationManager.PluginUninstalled -= InstallationManager_PluginUninstalled;
@@ -196,8 +211,6 @@ namespace Emby.Server.Implementations.EntryPoints
_installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
_appHost.HasPendingRestartChanged -= kernel_HasPendingRestartChanged;
- _syncManager.SyncJobCreated -= _syncManager_SyncJobCreated;
- _syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 103b4b321..6d73f98ad 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -1,5 +1,4 @@
-using System;
-using Emby.Server.Implementations.Browser;
+using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
@@ -40,17 +39,17 @@ namespace Emby.Server.Implementations.EntryPoints
return;
}
- if (_appHost.IsFirstRun)
+ if (!_config.Configuration.IsStartupWizardCompleted)
{
- BrowserLauncher.OpenDashboardPage("wizardstart.html", _appHost);
+ BrowserLauncher.OpenWebApp(_appHost);
}
- else if (_config.Configuration.IsStartupWizardCompleted && _config.Configuration.AutoRunWebApp)
+ else if (_config.Configuration.AutoRunWebApp)
{
var options = ((ApplicationHost)_appHost).StartupOptions;
if (!options.ContainsOption("-noautorunwebapp"))
{
- BrowserLauncher.OpenDashboardPage("index.html", _appHost);
+ BrowserLauncher.OpenWebApp(_appHost);
}
}
}
@@ -60,7 +59,6 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public void Dispose()
{
- GC.SuppressFinalize(this);
}
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
index 08f3edb3d..e27de8967 100644
--- a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
+++ b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
@@ -34,7 +34,6 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
_systemEvents.SystemShutdown -= _systemEvents_SystemShutdown;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index d04df0d2b..5edc5fade 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -45,6 +45,9 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public void Run()
{
+ // ToDo: Fix This
+ return;
+
var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
try
@@ -65,7 +68,6 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
index 11e806b0c..97feb32c0 100644
--- a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
@@ -61,17 +60,29 @@ namespace Emby.Server.Implementations.EntryPoints
var key = string.Join("_", keys.ToArray(keys.Count)).GetMD5();
- _apps.GetOrAdd(key, guid => GetNewClientInfo(session));
+ ClientInfo info;
+ if (!_apps.TryGetValue(key, out info))
+ {
+ info = new ClientInfo
+ {
+ AppName = session.Client,
+ AppVersion = session.ApplicationVersion,
+ DeviceName = session.DeviceName,
+ DeviceId = session.DeviceId
+ };
+
+ _apps[key] = info;
+
+ if (_config.Configuration.EnableAnonymousUsageReporting)
+ {
+ Task.Run(() => ReportNewSession(info));
+ }
+ }
}
}
- private async void ReportNewSession(ClientInfo client)
+ private async Task ReportNewSession(ClientInfo client)
{
- if (!_config.Configuration.EnableAnonymousUsageReporting)
- {
- return;
- }
-
try
{
await new UsageReporter(_applicationHost, _httpClient, _logger)
@@ -80,25 +91,10 @@ namespace Emby.Server.Implementations.EntryPoints
}
catch (Exception ex)
{
- _logger.ErrorException("Error sending anonymous usage statistics.", ex);
+ //_logger.ErrorException("Error sending anonymous usage statistics.", ex);
}
}
- private ClientInfo GetNewClientInfo(SessionInfo session)
- {
- var info = new ClientInfo
- {
- AppName = session.Client,
- AppVersion = session.ApplicationVersion,
- DeviceName = session.DeviceName,
- DeviceId = session.DeviceId
- };
-
- ReportNewSession(info);
-
- return info;
- }
-
public async void Run()
{
await Task.Delay(5000).ConfigureAwait(false);
@@ -123,14 +119,13 @@ namespace Emby.Server.Implementations.EntryPoints
}
catch (Exception ex)
{
- _logger.ErrorException("Error sending anonymous usage statistics.", ex);
+ //_logger.ErrorException("Error sending anonymous usage statistics.", ex);
}
}
public void Dispose()
{
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
index deee8d64b..86b335b77 100644
--- a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.EntryPoints
public async Task ReportAppUsage(ClientInfo app, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(app.DeviceId))
+ if (string.IsNullOrEmpty(app.DeviceId))
{
throw new ArgumentException("Client info must have a device Id");
}
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 13c72bf3c..36e29e46a 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ITimerFactory _timerFactory;
private const int UpdateDuration = 500;
- private readonly Dictionary<Guid, List<IHasUserData>> _changedItems = new Dictionary<Guid, List<IHasUserData>>();
+ private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>();
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager, ITimerFactory timerFactory)
{
@@ -62,22 +62,22 @@ namespace Emby.Server.Implementations.EntryPoints
UpdateTimer.Change(UpdateDuration, Timeout.Infinite);
}
- List<IHasUserData> keys;
+ List<BaseItem> keys;
if (!_changedItems.TryGetValue(e.UserId, out keys))
{
- keys = new List<IHasUserData>();
+ keys = new List<BaseItem>();
_changedItems[e.UserId] = keys;
}
keys.Add(e.Item);
- var baseItem = e.Item as BaseItem;
+ var baseItem = e.Item;
// Go up one level for indicators
if (baseItem != null)
{
- var parent = baseItem.IsOwnedItem ? baseItem.GetOwner() : baseItem.GetParent();
+ var parent = baseItem.GetOwner() ?? baseItem.GetParent();
if (parent != null)
{
@@ -105,50 +105,41 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- private async Task SendNotifications(IEnumerable<KeyValuePair<Guid, List<IHasUserData>>> changes, CancellationToken cancellationToken)
+ private async Task SendNotifications(List<KeyValuePair<Guid, List<BaseItem>>> changes, CancellationToken cancellationToken)
{
foreach (var pair in changes)
{
- var userId = pair.Key;
- var userSessions = _sessionManager.Sessions
- .Where(u => u.ContainsUser(userId) && u.SessionController != null && u.IsActive)
- .ToList();
+ await SendNotifications(pair.Key, pair.Value, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private Task SendNotifications(Guid userId, List<BaseItem> changedItems, CancellationToken cancellationToken)
+ {
+ return _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, "UserDataChanged", () => GetUserDataChangeInfo(userId, changedItems), cancellationToken);
+ }
+
+ private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)
+ {
+ var user = _userManager.GetUserById(userId);
- if (userSessions.Count > 0)
+ var dtoList = changedItems
+ .DistinctBy(i => i.Id)
+ .Select(i =>
{
- var user = _userManager.GetUserById(userId);
-
- var dtoList = pair.Value
- .DistinctBy(i => i.Id)
- .Select(i =>
- {
- var dto = _userDataManager.GetUserDataDto(i, user);
- dto.ItemId = i.Id.ToString("N");
- return dto;
- })
- .ToArray();
-
- var info = new UserDataChangeInfo
- {
- UserId = userId.ToString("N"),
+ var dto = _userDataManager.GetUserDataDto(i, user);
+ dto.ItemId = i.Id.ToString("N");
+ return dto;
+ })
+ .ToArray();
- UserDataList = dtoList
- };
+ var userIdString = userId.ToString("N");
- foreach (var userSession in userSessions)
- {
- try
- {
- await userSession.SessionController.SendUserDataChangeInfo(info, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending UserDataChanged message", ex);
- }
- }
- }
+ return new UserDataChangeInfo
+ {
+ UserId = userIdString,
- }
+ UserDataList = dtoList
+ };
}
public void Dispose()
@@ -160,7 +151,6 @@ namespace Emby.Server.Implementations.EntryPoints
}
_userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs
index f86279f37..583e93706 100644
--- a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs
+++ b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs
@@ -82,11 +82,6 @@ namespace Emby.Server.Implementations.EnvironmentInfo
return Environment.GetEnvironmentVariable(name);
}
- public virtual string GetUserId()
- {
- return null;
- }
-
public string StackTrace
{
get { return Environment.StackTrace; }
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
index 1d769acec..a1080a839 100644
--- a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
@@ -7,11 +7,9 @@ namespace Emby.Server.Implementations.FFMpeg
public string FFMpegFilename { get; set; }
public string FFProbeFilename { get; set; }
public string ArchiveType { get; set; }
- public string[] DownloadUrls { get; set; }
public FFMpegInstallInfo()
{
- DownloadUrls = new string[] { };
Version = "Path";
FFMpegFilename = "ffmpeg";
FFProbeFilename = "ffprobe";
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
index 9f4cd05fa..fe1df0953 100644
--- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
@@ -6,10 +6,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations;
-using Emby.Server.Implementations.FFMpeg;
namespace Emby.Server.Implementations.FFMpeg
{
@@ -32,7 +28,7 @@ namespace Emby.Server.Implementations.FFMpeg
_ffmpegInstallInfo = ffmpegInstallInfo;
}
- public async Task<FFMpegInfo> GetFFMpegInfo(StartupOptions options, IProgress<double> progress)
+ public FFMpegInfo GetFFMpegInfo(StartupOptions options)
{
var customffMpegPath = options.GetOption("-ffmpeg");
var customffProbePath = options.GetOption("-ffprobe");
@@ -49,8 +45,9 @@ namespace Emby.Server.Implementations.FFMpeg
var downloadInfo = _ffmpegInstallInfo;
- var prebuiltffmpeg = Path.Combine(_appPaths.ProgramSystemPath, downloadInfo.FFMpegFilename);
- var prebuiltffprobe = Path.Combine(_appPaths.ProgramSystemPath, downloadInfo.FFProbeFilename);
+ var prebuiltFolder = _appPaths.ProgramSystemPath;
+ var prebuiltffmpeg = Path.Combine(prebuiltFolder, downloadInfo.FFMpegFilename);
+ var prebuiltffprobe = Path.Combine(prebuiltFolder, downloadInfo.FFProbeFilename);
if (_fileSystem.FileExists(prebuiltffmpeg) && _fileSystem.FileExists(prebuiltffprobe))
{
return new FFMpegInfo
@@ -90,11 +87,7 @@ namespace Emby.Server.Implementations.FFMpeg
// No older version. Need to download and block until complete
if (existingVersion == null)
{
- var success = await DownloadFFMpeg(downloadInfo, versionedDirectoryPath, progress).ConfigureAwait(false);
- if (!success)
- {
- return new FFMpegInfo();
- }
+ return new FFMpegInfo();
}
else
{
@@ -144,99 +137,5 @@ namespace Emby.Server.Implementations.FFMpeg
return null;
}
-
- private async Task<bool> DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress<double> progress)
- {
- foreach (var url in downloadinfo.DownloadUrls)
- {
- progress.Report(0);
-
- try
- {
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- ExtractFFMpeg(downloadinfo, tempFile, directory);
- return true;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading {0}", ex, url);
- }
- }
- return false;
- }
-
- private void ExtractFFMpeg(FFMpegInstallInfo downloadinfo, string tempFile, string targetFolder)
- {
- _logger.Info("Extracting ffmpeg from {0}", tempFile);
-
- var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
-
- _fileSystem.CreateDirectory(tempFolder);
-
- try
- {
- ExtractArchive(downloadinfo, tempFile, tempFolder);
-
- var files = _fileSystem.GetFilePaths(tempFolder, true)
- .ToList();
-
- foreach (var file in files.Where(i =>
- {
- var filename = Path.GetFileName(i);
-
- return
- string.Equals(filename, downloadinfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(filename, downloadinfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase);
- }))
- {
- var targetFile = Path.Combine(targetFolder, Path.GetFileName(file));
- _fileSystem.CopyFile(file, targetFile, true);
- SetFilePermissions(targetFile);
- }
- }
- finally
- {
- DeleteFile(tempFile);
- }
- }
-
- private void SetFilePermissions(string path)
- {
- _fileSystem.SetExecutable(path);
- }
-
- private void ExtractArchive(FFMpegInstallInfo downloadinfo, string archivePath, string targetPath)
- {
- _logger.Info("Extracting {0} to {1}", archivePath, targetPath);
-
- if (string.Equals(downloadinfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase))
- {
- _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
- }
- else if (string.Equals(downloadinfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase))
- {
- _zipClient.ExtractAllFromTar(archivePath, targetPath, true);
- }
- }
-
- private void DeleteFile(string path)
- {
- try
- {
- _fileSystem.DeleteFile(path);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting temp file {0}", ex, path);
- }
- }
-
}
}
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 4a9e417f2..d53606e87 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -41,13 +41,12 @@ namespace Emby.Server.Implementations.HttpClientManager
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly Func<string> _defaultUserAgentFn;
/// <summary>
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
/// </summary>
- public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider, Func<string> defaultUserAgentFn)
+ public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, Func<string> defaultUserAgentFn)
{
if (appPaths == null)
{
@@ -60,7 +59,6 @@ namespace Emby.Server.Implementations.HttpClientManager
_logger = logger;
_fileSystem = fileSystem;
- _memoryStreamProvider = memoryStreamProvider;
_appPaths = appPaths;
_defaultUserAgentFn = defaultUserAgentFn;
@@ -310,7 +308,7 @@ namespace Emby.Server.Implementations.HttpClientManager
{
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
- var memoryStream = _memoryStreamProvider.CreateNew();
+ var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
@@ -343,7 +341,7 @@ namespace Emby.Server.Implementations.HttpClientManager
using (var responseStream = response.Content)
{
- var memoryStream = _memoryStreamProvider.CreateNew();
+ var memoryStream = new MemoryStream();
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
@@ -458,7 +456,7 @@ namespace Emby.Server.Implementations.HttpClientManager
using (var stream = httpResponse.GetResponseStream())
{
- var memoryStream = _memoryStreamProvider.CreateNew();
+ var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
@@ -636,7 +634,7 @@ namespace Emby.Server.Implementations.HttpClientManager
{
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
- await StreamHelper.CopyToAsync(httpResponse.GetResponseStream(), fs, StreamDefaults.DefaultCopyToBufferSize, options.Progress, contentLength.Value, options.CancellationToken).ConfigureAwait(false);
+ await httpResponse.GetResponseStream().CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
}
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index aa679e1b9..353ba5282 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
+using System.Linq;
namespace Emby.Server.Implementations.HttpServer
{
@@ -147,6 +148,13 @@ namespace Emby.Server.Implementations.HttpServer
}
}
+ private string[] SkipLogExtensions = new string[]
+ {
+ ".js",
+ ".html",
+ ".css"
+ };
+
public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken)
{
try
@@ -157,17 +165,24 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
+ var path = Path;
+
if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1))
{
- Logger.Info("Transmit file {0}", Path);
+ var extension = System.IO.Path.GetExtension(path);
+
+ if (extension == null || !SkipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Transmit file {0}", path);
+ }
//var count = FileShare == FileShareMode.ReadWrite ? TotalContentLength : 0;
- await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false);
+ await response.TransmitFile(path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false);
return;
}
- await response.TransmitFile(Path, RangeStart, RangeLength, FileShare, cancellationToken).ConfigureAwait(false);
+ await response.TransmitFile(path, RangeStart, RangeLength, FileShare, cancellationToken).ConfigureAwait(false);
}
finally
{
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 937eb8029..0093258e3 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -12,7 +12,6 @@ using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer.SocketSharp;
using Emby.Server.Implementations.Services;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Security;
@@ -25,6 +24,10 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
+using System.Net.Sockets;
+using Emby.Server.Implementations.Net;
+using MediaBrowser.Common.Events;
+using MediaBrowser.Model.Events;
namespace Emby.Server.Implementations.HttpServer
{
@@ -35,64 +38,47 @@ namespace Emby.Server.Implementations.HttpServer
private readonly ILogger _logger;
public string[] UrlPrefixes { get; private set; }
- private readonly List<IService> _restServices = new List<IService>();
-
private IHttpListener _listener;
- public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
- public event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting;
+ public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly IServerApplicationHost _appHost;
private readonly ITextEncoding _textEncoding;
- private readonly ISocketFactory _socketFactory;
- private readonly ICryptoProvider _cryptoProvider;
- private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
private readonly IXmlSerializer _xmlSerializer;
- private readonly X509Certificate _certificate;
- private readonly IEnvironmentInfo _environment;
private readonly Func<Type, Func<string, object>> _funcParseFn;
- private readonly bool _enableDualModeSockets;
- public Action<IRequest, IResponse, object>[] RequestFilters { get; set; }
public Action<IRequest, IResponse, object>[] ResponseFilters { get; set; }
private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
public static HttpListenerHost Instance { get; protected set; }
+ private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
+ private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
+
public HttpListenerHost(IServerApplicationHost applicationHost,
ILogger logger,
IServerConfigurationManager config,
- string serviceName,
- string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, X509Certificate certificate, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem)
+ string defaultRedirectPath, INetworkManager networkManager, ITextEncoding textEncoding, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, Func<Type, Func<string, object>> funcParseFn)
{
Instance = this;
_appHost = applicationHost;
DefaultRedirectPath = defaultRedirectPath;
_networkManager = networkManager;
- _memoryStreamProvider = memoryStreamProvider;
_textEncoding = textEncoding;
- _socketFactory = socketFactory;
- _cryptoProvider = cryptoProvider;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
- _environment = environment;
- _certificate = certificate;
- _funcParseFn = funcParseFn;
- _enableDualModeSockets = enableDualModeSockets;
- _fileSystem = fileSystem;
_config = config;
_logger = logger;
+ _funcParseFn = funcParseFn;
- RequestFilters = new Action<IRequest, IResponse, object>[] { };
ResponseFilters = new Action<IRequest, IResponse, object>[] { };
}
@@ -140,12 +126,6 @@ namespace Emby.Server.Implementations.HttpServer
attribute.RequestFilter(req, res, requestDto);
}
- //Exec global filters
- foreach (var requestFilter in RequestFilters)
- {
- requestFilter(req, res, requestDto);
- }
-
//Exec remaining RequestFilter attributes with Priority >= 0
for (; i < count && attributes[i].Priority >= 0; i++)
{
@@ -181,45 +161,38 @@ namespace Emby.Server.Implementations.HttpServer
return attributes;
}
- private IHttpListener GetListener()
- {
- //return new KestrelHost.KestrelListener(_logger, _environment, _fileSystem);
-
- return new WebSocketSharpListener(_logger,
- _certificate,
- _memoryStreamProvider,
- _textEncoding,
- _networkManager,
- _socketFactory,
- _cryptoProvider,
- _enableDualModeSockets,
- _fileSystem,
- _environment);
- }
-
- private void OnWebSocketConnecting(WebSocketConnectingEventArgs args)
+ private void OnWebSocketConnected(WebSocketConnectEventArgs e)
{
if (_disposed)
{
return;
}
- if (WebSocketConnecting != null)
+ var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger, _textEncoding)
{
- WebSocketConnecting(this, args);
- }
- }
+ OnReceive = ProcessWebSocketMessageReceived,
+ Url = e.Url,
+ QueryString = e.QueryString ?? new QueryParamCollection()
+ };
- private void OnWebSocketConnected(WebSocketConnectEventArgs args)
- {
- if (_disposed)
+ connection.Closed += Connection_Closed;
+
+ lock (_webSocketConnections)
{
- return;
+ _webSocketConnections.Add(connection);
}
if (WebSocketConnected != null)
{
- WebSocketConnected(this, args);
+ EventHelper.FireEventIfNotNull(WebSocketConnected, this, new GenericEventArgs<IWebSocketConnection>(connection), _logger);
+ }
+ }
+
+ private void Connection_Closed(object sender, EventArgs e)
+ {
+ lock (_webSocketConnections)
+ {
+ _webSocketConnections.Remove((IWebSocketConnection)sender);
}
}
@@ -271,16 +244,20 @@ namespace Emby.Server.Implementations.HttpServer
return statusCode;
}
- private void ErrorHandler(Exception ex, IRequest httpReq, bool logException = true)
+ private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage)
{
try
{
ex = GetActualException(ex);
- if (logException)
+ if (logExceptionStackTrace)
{
_logger.ErrorException("Error processing request", ex);
}
+ else if (logExceptionMessage)
+ {
+ _logger.Error(ex.Message);
+ }
var httpRes = httpReq.Response;
@@ -293,7 +270,7 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.StatusCode = statusCode;
httpRes.ContentType = "text/html";
- Write(httpRes, ex.Message);
+ await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
}
catch
{
@@ -301,11 +278,46 @@ namespace Emby.Server.Implementations.HttpServer
}
}
+ private string NormalizeExceptionMessage(string msg)
+ {
+ if (msg == null)
+ {
+ return string.Empty;
+ }
+
+ // Strip any information we don't want to reveal
+
+ msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase);
+ msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
+
+ return msg;
+ }
+
/// <summary>
/// Shut down the Web Service
/// </summary>
public void Stop()
{
+ List<IWebSocketConnection> connections;
+
+ lock (_webSocketConnections)
+ {
+ connections = _webSocketConnections.ToList();
+ _webSocketConnections.Clear();
+ }
+
+ foreach (var connection in connections)
+ {
+ try
+ {
+ connection.Dispose();
+ }
+ catch
+ {
+
+ }
+ }
+
if (_listener != null)
{
_logger.Info("Stopping HttpListener...");
@@ -329,9 +341,9 @@ namespace Emby.Server.Implementations.HttpServer
{
var extension = GetExtension(url);
- if (string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension))
+ if (string.IsNullOrEmpty(extension) || !_skipLogExtensions.ContainsKey(extension))
{
- if (string.IsNullOrWhiteSpace(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
+ if (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
{
return true;
}
@@ -422,12 +434,53 @@ namespace Emby.Server.Implementations.HttpServer
return true;
}
+ private bool ValidateRequest(string remoteIp, bool isLocal)
+ {
+ if (isLocal)
+ {
+ return true;
+ }
+
+ if (_config.Configuration.EnableRemoteAccess)
+ {
+ var addressFilter = _config.Configuration.RemoteIPFilter.Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+
+ if (addressFilter.Length > 0 && !_networkManager.IsInLocalNetwork(remoteIp))
+ {
+ if (_config.Configuration.IsRemoteIPFilterBlacklist)
+ {
+ return !_networkManager.IsAddressInSubnets(remoteIp, addressFilter);
+ }
+ else
+ {
+ return _networkManager.IsAddressInSubnets(remoteIp, addressFilter);
+ }
+ }
+ }
+ else
+ {
+ if (!_networkManager.IsInLocalNetwork(remoteIp))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private bool ValidateSsl(string remoteIp, string urlString)
{
- if (_config.Configuration.RequireHttps && _appHost.EnableHttps)
+ if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy)
{
if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1)
{
+ // These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected
+ if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1 ||
+ urlString.IndexOf("dlna/", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return true;
+ }
+
if (!_networkManager.IsInLocalNetwork(remoteIp))
{
return false;
@@ -448,7 +501,7 @@ namespace Emby.Server.Implementations.HttpServer
bool enableLog = false;
bool logHeaders = false;
string urlToLog = null;
- string remoteIp = null;
+ string remoteIp = httpReq.RemoteIp;
try
{
@@ -456,7 +509,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 503;
httpRes.ContentType = "text/plain";
- Write(httpRes, "Server shutting down");
+ await Write(httpRes, "Server shutting down").ConfigureAwait(false);
return;
}
@@ -464,17 +517,21 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 400;
httpRes.ContentType = "text/plain";
- Write(httpRes, "Invalid host");
+ await Write(httpRes, "Invalid host").ConfigureAwait(false);
return;
}
- if (!ValidateSsl(httpReq.RemoteIp, urlString))
+ if (!ValidateRequest(remoteIp, httpReq.IsLocal))
{
- var httpsUrl = urlString
- .Replace("http://", "https://", StringComparison.OrdinalIgnoreCase)
- .Replace(":" + _config.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture), ":" + _config.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase);
+ httpRes.StatusCode = 403;
+ httpRes.ContentType = "text/plain";
+ await Write(httpRes, "Forbidden").ConfigureAwait(false);
+ return;
+ }
- RedirectToUrl(httpRes, httpsUrl);
+ if (!ValidateSsl(httpReq.RemoteIp, urlString))
+ {
+ RedirectToSecureUrl(httpReq, httpRes, urlString);
return;
}
@@ -485,7 +542,7 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
httpRes.ContentType = "text/plain";
- Write(httpRes, string.Empty);
+ await Write(httpRes, string.Empty).ConfigureAwait(false);
return;
}
@@ -498,7 +555,6 @@ namespace Emby.Server.Implementations.HttpServer
if (enableLog)
{
urlToLog = GetUrlToLog(urlString);
- remoteIp = httpReq.RemoteIp;
LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent, logHeaders ? httpReq.Headers : null);
}
@@ -527,9 +583,9 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{
- Write(httpRes,
+ await Write(httpRes,
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
- newUrl + "\">" + newUrl + "</a></body></html>");
+ newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
return;
}
}
@@ -544,9 +600,9 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{
- Write(httpRes,
+ await Write(httpRes,
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
- newUrl + "\">" + newUrl + "</a></body></html>");
+ newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
return;
}
}
@@ -572,18 +628,29 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
- if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(httpReq.QueryString["r"], "0", StringComparison.OrdinalIgnoreCase))
{
- RedirectToUrl(httpRes, "web/pin.html");
- return;
+ if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, "index.html#!/dashboard.html");
+ }
+
+ if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, "index.html");
+ }
}
- if (!string.IsNullOrWhiteSpace(GlobalResponse))
+ if (!string.IsNullOrEmpty(GlobalResponse))
{
- httpRes.StatusCode = 503;
- httpRes.ContentType = "text/html";
- Write(httpRes, GlobalResponse);
- return;
+ // We don't want the address pings in ApplicationHost to fail
+ if (localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ httpRes.StatusCode = 503;
+ httpRes.ContentType = "text/html";
+ await Write(httpRes, GlobalResponse).ConfigureAwait(false);
+ return;
+ }
}
var handler = GetServiceHandler(httpReq);
@@ -594,23 +661,34 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- ErrorHandler(new FileNotFoundException(), httpReq, false);
+ await ErrorHandler(new FileNotFoundException(), httpReq, false, false).ConfigureAwait(false);
}
}
catch (OperationCanceledException ex)
{
- ErrorHandler(ex, httpReq, false);
+ await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
+ }
+
+ catch (IOException ex)
+ {
+ await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
+ }
+
+ catch (SocketException ex)
+ {
+ await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false);
+ }
+
+ catch (SecurityException ex)
+ {
+ await ErrorHandler(ex, httpReq, false, true).ConfigureAwait(false);
}
catch (Exception ex)
{
var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
-#if DEBUG
- logException = true;
-#endif
-
- ErrorHandler(ex, httpReq, logException);
+ await ErrorHandler(ex, httpReq, logException, false).ConfigureAwait(false);
}
finally
{
@@ -655,13 +733,36 @@ namespace Emby.Server.Implementations.HttpServer
return null;
}
- private void Write(IResponse response, string text)
+ private Task Write(IResponse response, string text)
{
var bOutput = Encoding.UTF8.GetBytes(text);
response.SetContentLength(bOutput.Length);
- var outputStream = response.OutputStream;
- outputStream.Write(bOutput, 0, bOutput.Length);
+ return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
+ }
+
+ private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url)
+ {
+ int currentPort;
+ Uri uri;
+ if (Uri.TryCreate(url, UriKind.Absolute, out uri))
+ {
+ currentPort = uri.Port;
+ var builder = new UriBuilder(uri);
+ builder.Port = _config.Configuration.PublicHttpsPort;
+ builder.Scheme = "https";
+ url = builder.Uri.ToString();
+
+ RedirectToUrl(httpRes, url);
+ }
+ else
+ {
+ var httpsUrl = url
+ .Replace("http://", "https://", StringComparison.OrdinalIgnoreCase)
+ .Replace(":" + _config.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture), ":" + _config.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase);
+
+ RedirectToUrl(httpRes, url);
+ }
}
public static void RedirectToUrl(IResponse httpRes, string url)
@@ -676,26 +777,18 @@ namespace Emby.Server.Implementations.HttpServer
/// Adds the rest handlers.
/// </summary>
/// <param name="services">The services.</param>
- public void Init(IEnumerable<IService> services)
+ public void Init(IEnumerable<IService> services, IEnumerable<IWebSocketListener> listeners)
{
- _restServices.AddRange(services);
+ _webSocketListeners = listeners.ToArray();
ServiceController = new ServiceController();
_logger.Info("Calling ServiceStack AppHost.Init");
- var types = _restServices.Select(r => r.GetType()).ToArray();
+ var types = services.Select(r => r.GetType()).ToArray();
ServiceController.Init(this, types);
- var list = new List<Action<IRequest, IResponse, object>>();
- foreach (var filter in _appHost.GetExports<IRequestFilter>())
- {
- list.Add(filter.Filter);
- }
-
- RequestFilters = list.ToArray();
-
ResponseFilters = new Action<IRequest, IResponse, object>[]
{
new ResponseFilter(_logger).FilterResponse
@@ -750,12 +843,12 @@ namespace Emby.Server.Implementations.HttpServer
_xmlSerializer.SerializeToStream(o, stream);
}
- public object DeserializeXml(Type type, Stream stream)
+ public Task<object> DeserializeXml(Type type, Stream stream)
{
- return _xmlSerializer.DeserializeFromStream(type, stream);
+ return Task.FromResult(_xmlSerializer.DeserializeFromStream(type, stream));
}
- public object DeserializeJson(Type type, Stream stream)
+ public Task<object> DeserializeJson(Type type, Stream stream)
{
//using (var reader = new StreamReader(stream))
//{
@@ -763,7 +856,7 @@ namespace Emby.Server.Implementations.HttpServer
// Logger.Info(json);
// return _jsonSerializer.DeserializeFromString(json, type);
//}
- return _jsonSerializer.DeserializeFromStream(stream, type);
+ return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
}
private string NormalizeEmbyRoutePath(string path)
@@ -815,20 +908,46 @@ namespace Emby.Server.Implementations.HttpServer
}
}
+ /// <summary>
+ /// Processes the web socket message received.
+ /// </summary>
+ /// <param name="result">The result.</param>
+ private Task ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
+ {
+ if (_disposed)
+ {
+ return Task.CompletedTask;
+ }
+
+ //_logger.Debug("Websocket message received: {0}", result.MessageType);
+
+ var tasks = _webSocketListeners.Select(i => Task.Run(async () =>
+ {
+ try
+ {
+ await i.ProcessMessage(result).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("{0} failed processing WebSocket message {1}", ex, i.GetType().Name, result.MessageType ?? string.Empty);
+ }
+ }));
+
+ return Task.WhenAll(tasks);
+ }
+
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
- public void StartServer(string[] urlPrefixes)
+ public void StartServer(string[] urlPrefixes, IHttpListener httpListener)
{
UrlPrefixes = urlPrefixes;
- _listener = GetListener();
+ _listener = httpListener;
_listener.WebSocketConnected = OnWebSocketConnected;
- _listener.WebSocketConnecting = OnWebSocketConnecting;
_listener.ErrorHandler = ErrorHandler;
_listener.RequestHandler = RequestHandler;
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 86deccee1..df493b4c3 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -6,6 +6,7 @@ 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;
@@ -30,16 +31,17 @@ namespace Emby.Server.Implementations.HttpServer
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
+
+ private IBrotliCompressor _brotliCompressor;
/// <summary>
/// Initializes a new instance of the <see cref="HttpResultFactory" /> class.
/// </summary>
- public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamFactory)
+ public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IBrotliCompressor brotliCompressor)
{
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
- _memoryStreamFactory = memoryStreamFactory;
+ _brotliCompressor = brotliCompressor;
_logger = logManager.GetLogger("HttpResultFactory");
}
@@ -50,9 +52,24 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="contentType">Type of the content.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <returns>System.Object.</returns>
- public object GetResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
+ public object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null)
{
- return GetHttpResult(content, contentType, true, responseHeaders);
+ return GetHttpResult(null, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
}
public object GetRedirectResult(string url)
@@ -60,7 +77,7 @@ namespace Emby.Server.Implementations.HttpServer
var responseHeaders = new Dictionary<string, string>();
responseHeaders["Location"] = url;
- var result = new HttpResult(new byte[] { }, "text/plain", HttpStatusCode.Redirect);
+ var result = new HttpResult(Array.Empty<byte>(), "text/plain", HttpStatusCode.Redirect);
AddResponseHeaders(result, responseHeaders);
@@ -70,39 +87,98 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Gets the HTTP result.
/// </summary>
- private IHasHeaders GetHttpResult(object content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
- IHasHeaders result;
+ var result = new StreamWriter(content, contentType, _logger);
- var stream = content as Stream;
+ if (responseHeaders == null)
+ {
+ responseHeaders = new Dictionary<string, string>();
+ }
- if (stream != null)
+ string expires;
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires))
{
- result = new StreamWriter(stream, contentType, _logger);
+ responseHeaders["Expires"] = "-1";
}
- else
+ AddResponseHeaders(result, responseHeaders);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the HTTP result.
+ /// </summary>
+ private IHasHeaders GetHttpResult(IRequest requestContext, byte[] content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ {
+ IHasHeaders result;
+
+ var compressionType = requestContext == null ? null : GetCompressionType(requestContext, content, contentType);
+
+ var isHeadRequest = string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
+
+ if (string.IsNullOrEmpty(compressionType))
{
- var bytes = content as byte[];
+ var contentLength = content.Length;
- if (bytes != null)
+ if (isHeadRequest)
{
- result = new StreamWriter(bytes, contentType, _logger);
+ content = Array.Empty<byte>();
}
- else
- {
- var text = content as string;
- if (text != null)
- {
- result = new StreamWriter(Encoding.UTF8.GetBytes(text), contentType, _logger);
- }
- else
- {
- result = new HttpResult(content, contentType, HttpStatusCode.OK);
- }
+ result = new StreamWriter(content, contentType, contentLength, _logger);
+ }
+ else
+ {
+ result = GetCompressedResult(content, compressionType, responseHeaders, isHeadRequest, contentType);
+ }
+
+ if (responseHeaders == null)
+ {
+ responseHeaders = new Dictionary<string, string>();
+ }
+
+ string expires;
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires))
+ {
+ responseHeaders["Expires"] = "-1";
+ }
+
+ AddResponseHeaders(result, responseHeaders);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the HTTP result.
+ /// </summary>
+ private IHasHeaders GetHttpResult(IRequest requestContext, string content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ {
+ IHasHeaders result;
+
+ var bytes = Encoding.UTF8.GetBytes(content);
+
+ var compressionType = requestContext == null ? null : GetCompressionType(requestContext, bytes, contentType);
+
+ var isHeadRequest = requestContext == null ? false : string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
+
+ if (string.IsNullOrEmpty(compressionType))
+ {
+ var contentLength = bytes.Length;
+
+ if (isHeadRequest)
+ {
+ bytes = Array.Empty<byte>();
}
+
+ result = new StreamWriter(bytes, contentType, contentLength, _logger);
}
+ else
+ {
+ result = GetCompressedResult(bytes, compressionType, responseHeaders, isHeadRequest, contentType);
+ }
+
if (responseHeaders == null)
{
responseHeaders = new Dictionary<string, string>();
@@ -123,20 +199,9 @@ namespace Emby.Server.Implementations.HttpServer
/// Gets the optimized result.
/// </summary>
/// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="result">The result.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">result</exception>
- public object GetOptimizedResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
+ public object GetResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
where T : class
{
- return GetOptimizedResultInternal<T>(requestContext, result, true, responseHeaders);
- }
-
- private object GetOptimizedResultInternal<T>(IRequest requestContext, T result, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
- where T : class
- {
if (result == null)
{
throw new ArgumentNullException("result");
@@ -147,24 +212,49 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- if (addCachePrevention)
+ responseHeaders["Expires"] = "-1";
+
+ return ToOptimizedResultInternal(requestContext, result, responseHeaders);
+ }
+
+ private string GetCompressionType(IRequest request, byte[] content, string responseContentType)
+ {
+ if (responseContentType == null)
{
- responseHeaders["Expires"] = "-1";
+ return null;
}
- return ToOptimizedResultInternal(requestContext, result, responseHeaders);
+ // Per apple docs, hls manifests must be compressed
+ if (!responseContentType.StartsWith("text/", StringComparison.OrdinalIgnoreCase) &&
+ responseContentType.IndexOf("json", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("xml", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("application/x-mpegURL", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ return null;
+ }
+
+ if (content.Length < 1024)
+ {
+ return null;
+ }
+
+ return GetCompressionType(request);
}
- public static string GetCompressionType(IRequest request)
+ private string GetCompressionType(IRequest request)
{
var acceptEncoding = request.Headers["Accept-Encoding"];
- if (!string.IsNullOrWhiteSpace(acceptEncoding))
+ if (acceptEncoding != null)
{
- if (acceptEncoding.Contains("deflate"))
+ //if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1)
+ // return "br";
+
+ if (acceptEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) != -1)
return "deflate";
- if (acceptEncoding.Contains("gzip"))
+ if (acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1)
return "gzip";
}
@@ -180,7 +270,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public object ToOptimizedResult<T>(IRequest request, T dto)
{
- return ToOptimizedResultInternal(request, dto, null);
+ return ToOptimizedResultInternal(request, dto);
}
private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
@@ -192,153 +282,137 @@ namespace Emby.Server.Implementations.HttpServer
case "application/xml":
case "text/xml":
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
- return GetHttpResult(SerializeToXmlString(dto), contentType, false, responseHeaders);
+ return GetHttpResult(request, SerializeToXmlString(dto), contentType, false, responseHeaders);
case "application/json":
case "text/json":
- return GetHttpResult(_jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
+ return GetHttpResult(request, _jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
default:
- {
- var ms = new MemoryStream();
- var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
-
- writerFn(dto, ms);
+ break;
+ }
- ms.Position = 0;
+ var isHeadRequest = string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase);
- if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
- {
- return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
- }
+ var ms = new MemoryStream();
+ var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
- return GetHttpResult(ms, contentType, true, responseHeaders);
- }
- }
- }
+ writerFn(dto, ms);
- public static string GetRealContentType(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLower().Trim();
- }
+ ms.Position = 0;
- private string SerializeToXmlString(object from)
- {
- using (var ms = new MemoryStream())
+ if (isHeadRequest)
{
- var xwSettings = new XmlWriterSettings();
- xwSettings.Encoding = new UTF8Encoding(false);
- xwSettings.OmitXmlDeclaration = false;
-
- using (var xw = XmlWriter.Create(ms, xwSettings))
+ using (ms)
{
- var serializer = new DataContractSerializer(from.GetType());
- serializer.WriteObject(xw, from);
- xw.Flush();
- ms.Seek(0, SeekOrigin.Begin);
- var reader = new StreamReader(ms);
- return reader.ReadToEnd();
+ return GetHttpResult(request, Array.Empty<byte>(), contentType, true, responseHeaders);
}
}
+
+ return GetHttpResult(request, ms, contentType, true, responseHeaders);
}
- /// <summary>
- /// Gets the optimized result using cache.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey
- /// or
- /// factoryFn</exception>
- public object GetOptimizedResultUsingCache<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, IDictionary<string, string> responseHeaders = null)
- where T : class
+ private IHasHeaders GetCompressedResult(byte[] content,
+ string requestedCompressionType,
+ IDictionary<string, string> responseHeaders,
+ bool isHeadRequest,
+ string contentType)
{
- if (cacheKey == Guid.Empty)
- {
- throw new ArgumentNullException("cacheKey");
- }
- if (factoryFn == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
-
- var key = cacheKey.ToString("N");
-
if (responseHeaders == null)
{
responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, null);
+ content = Compress(content, requestedCompressionType);
+ responseHeaders["Content-Encoding"] = requestedCompressionType;
- if (result != null)
- {
- return result;
- }
+ responseHeaders["Vary"] = "Accept-Encoding";
- return GetOptimizedResultInternal(requestContext, factoryFn(), false, responseHeaders);
- }
+ var contentLength = content.Length;
- /// <summary>
- /// To the cached result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey</exception>
- public object GetCachedResult<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, string contentType, IDictionary<string, string> responseHeaders = null)
- where T : class
- {
- if (cacheKey == Guid.Empty)
+ if (isHeadRequest)
{
- throw new ArgumentNullException("cacheKey");
+ var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength, _logger);
+ AddResponseHeaders(result, responseHeaders);
+ return result;
}
- if (factoryFn == null)
+ else
{
- throw new ArgumentNullException("factoryFn");
+ var result = new StreamWriter(content, contentType, contentLength, _logger);
+ AddResponseHeaders(result, responseHeaders);
+ return result;
}
+ }
- var key = cacheKey.ToString("N");
+ private byte[] Compress(byte[] bytes, string compressionType)
+ {
+ if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase))
+ return CompressBrotli(bytes);
- if (responseHeaders == null)
- {
- responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
+ if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
+ return Deflate(bytes);
+
+ if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
+ return GZip(bytes);
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, contentType);
+ throw new NotSupportedException(compressionType);
+ }
+
+ private byte[] CompressBrotli(byte[] bytes)
+ {
+ return _brotliCompressor.Compress(bytes);
+ }
- if (result != null)
+ 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))
{
- return result;
+ zipStream.Write(bytes, 0, bytes.Length);
+ zipStream.Dispose();
+
+ return ms.ToArray();
}
+ }
- result = factoryFn();
+ 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();
- // Apply caching headers
- var hasHeaders = result as IHasHeaders;
+ return ms.ToArray();
+ }
+ }
- if (hasHeaders != null)
+ public static string GetRealContentType(string contentType)
+ {
+ return contentType == null
+ ? null
+ : contentType.Split(';')[0].ToLower().Trim();
+ }
+
+ private string SerializeToXmlString(object from)
+ {
+ using (var ms = new MemoryStream())
{
- AddResponseHeaders(hasHeaders, responseHeaders);
- return hasHeaders;
- }
+ var xwSettings = new XmlWriterSettings();
+ xwSettings.Encoding = new UTF8Encoding(false);
+ xwSettings.OmitXmlDeclaration = false;
- return GetHttpResult(result, contentType, false, responseHeaders);
+ using (var xw = XmlWriter.Create(ms, xwSettings))
+ {
+ var serializer = new DataContractSerializer(from.GetType());
+ serializer.WriteObject(xw, from);
+ xw.Flush();
+ ms.Seek(0, SeekOrigin.Begin);
+ var reader = new StreamReader(ms);
+ return reader.ReadToEnd();
+ }
+ }
}
/// <summary>
@@ -357,7 +431,7 @@ namespace Emby.Server.Implementations.HttpServer
AddAgeHeader(responseHeaders, lastDateModified);
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
- var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
+ var result = new HttpResult(Array.Empty<byte>(), contentType ?? "text/html", HttpStatusCode.NotModified);
AddResponseHeaders(result, responseHeaders);
@@ -402,7 +476,7 @@ namespace Emby.Server.Implementations.HttpServer
throw new ArgumentException("FileShare must be either Read or ReadWrite");
}
- if (string.IsNullOrWhiteSpace(options.ContentType))
+ if (string.IsNullOrEmpty(options.ContentType))
{
options.ContentType = MimeTypes.GetMimeType(path);
}
@@ -460,19 +534,17 @@ namespace Emby.Server.Implementations.HttpServer
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var contentType = options.ContentType;
- if (cacheKey == Guid.Empty)
+ if (!cacheKey.Equals(Guid.Empty))
{
- throw new ArgumentNullException("cacheKey");
- }
+ var key = cacheKey.ToString("N");
- var key = cacheKey.ToString("N");
+ // See if the result is already cached in the browser
+ var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
-
- if (result != null)
- {
- return result;
+ if (result != null)
+ {
+ return result;
+ }
}
// TODO: We don't really need the option value
@@ -484,7 +556,7 @@ namespace Emby.Server.Implementations.HttpServer
var rangeHeader = requestContext.Headers.Get("Range");
- if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path))
+ if (!isHeadRequest && !string.IsNullOrEmpty(options.Path))
{
var hasHeaders = new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem)
{
@@ -497,11 +569,24 @@ namespace Emby.Server.Implementations.HttpServer
return hasHeaders;
}
- if (!string.IsNullOrWhiteSpace(rangeHeader))
+ var stream = await factoryFn().ConfigureAwait(false);
+
+ var totalContentLength = options.ContentLength;
+ if (!totalContentLength.HasValue)
{
- var stream = await factoryFn().ConfigureAwait(false);
+ try
+ {
+ totalContentLength = stream.Length;
+ }
+ catch (NotSupportedException)
+ {
- var hasHeaders = new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger)
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
+ {
+ var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
{
OnComplete = options.OnComplete
};
@@ -511,15 +596,17 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- var stream = await factoryFn().ConfigureAwait(false);
-
- responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
+ if (totalContentLength.HasValue)
+ {
+ responseHeaders["Content-Length"] = totalContentLength.Value.ToString(UsCulture);
+ }
if (isHeadRequest)
{
- stream.Dispose();
-
- return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
+ using (stream)
+ {
+ return GetHttpResult(requestContext, Array.Empty<byte>(), contentType, true, responseHeaders);
+ }
}
var hasHeaders = new StreamWriter(stream, contentType, _logger)
@@ -603,7 +690,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="lastDateModified">The last date modified.</param>
/// <param name="cacheDuration">Duration of the cache.</param>
/// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(IRequest requestContext, Guid? cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
+ private bool IsNotModified(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
{
//var isNotModified = true;
@@ -624,8 +711,10 @@ namespace Emby.Server.Implementations.HttpServer
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
+ var hasCacheKey = !cacheKey.Equals(Guid.Empty);
+
// Validate If-None-Match
- if ((cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader)))
+ if ((hasCacheKey || !string.IsNullOrEmpty(ifNoneMatchHeader)))
{
Guid ifNoneMatch;
@@ -633,7 +722,7 @@ namespace Emby.Server.Implementations.HttpServer
if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch))
{
- if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch)
+ if (hasCacheKey && cacheKey.Equals(ifNoneMatch))
{
return true;
}
@@ -697,4 +786,9 @@ namespace Emby.Server.Implementations.HttpServer
}
}
}
+
+ public interface IBrotliCompressor
+ {
+ byte[] Compress(byte[] content);
+ }
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
index 9feb2311d..e21607ebd 100644
--- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using Emby.Server.Implementations.Net;
namespace Emby.Server.Implementations.HttpServer
{
@@ -13,7 +14,7 @@ namespace Emby.Server.Implementations.HttpServer
/// Gets or sets the error handler.
/// </summary>
/// <value>The error handler.</value>
- Action<Exception, IRequest, bool> ErrorHandler { get; set; }
+ Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
/// <summary>
/// Gets or sets the request handler.
diff --git a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
index 46bb4c7f9..3aa48efd4 100644
--- a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
+++ b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
@@ -2,24 +2,11 @@
using System;
using System.Globalization;
using MediaBrowser.Model.Services;
-using SocketHttpListener.Net;
namespace Emby.Server.Implementations.HttpServer
{
public static class LoggerUtils
{
- /// <summary>
- /// Logs the request.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="request">The request.</param>
- public static void LogRequest(ILogger logger, HttpListenerRequest request)
- {
- var url = request.Url.ToString();
-
- logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
- }
-
public static void LogRequest(ILogger logger, string url, string method, string userAgent, QueryParamCollection headers)
{
if (headers == null)
diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 7c967949b..4177c7e78 100644
--- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -58,7 +58,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
- public RangeRequestWriter(string rangeHeader, Stream source, string contentType, bool isHeadRequest, ILogger logger)
+ public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -76,17 +76,17 @@ namespace Emby.Server.Implementations.HttpServer
StatusCode = HttpStatusCode.PartialContent;
Cookies = new List<Cookie>();
- SetRangeValues();
+ SetRangeValues(contentLength);
}
/// <summary>
/// Sets the range values.
/// </summary>
- private void SetRangeValues()
+ private void SetRangeValues(long contentLength)
{
var requestedRange = RequestedRanges[0];
- TotalContentLength = SourceStream.Length;
+ TotalContentLength = contentLength;
// If the requested range is "0-", we can optimize by just doing a stream copy
if (!requestedRange.Value.HasValue)
@@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.HttpServer
Headers["Content-Length"] = RangeLength.ToString(UsCulture);
Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength);
- if (RangeStart > 0)
+ if (RangeStart > 0 && SourceStream.CanSeek)
{
SourceStream.Position = RangeStart;
}
diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
index ac36f8f51..385d19b6b 100644
--- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
+++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
@@ -2,7 +2,6 @@
using System;
using System.Globalization;
using System.Text;
-using Emby.Server.Implementations.HttpServer.SocketSharp;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer
@@ -47,7 +46,6 @@ namespace Emby.Server.Implementations.HttpServer
}
var hasHeaders = dto as IHasHeaders;
- var sharpResponse = res as WebSocketSharpResponse;
if (hasHeaders != null)
{
@@ -67,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer
if (length > 0)
{
res.SetContentLength(length);
-
+
//var listenerResponse = res.OriginalResponse as HttpListenerResponse;
//if (listenerResponse != null)
@@ -78,10 +76,7 @@ namespace Emby.Server.Implementations.HttpServer
// return;
//}
- if (sharpResponse != null)
- {
- sharpResponse.SendChunked = false;
- }
+ res.SendChunked = false;
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index fadab4482..8f6d042dd 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Session;
using System;
using System.Linq;
using MediaBrowser.Model.Services;
+using MediaBrowser.Common.Net;
namespace Emby.Server.Implementations.HttpServer.Security
{
@@ -16,21 +17,21 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
private readonly IServerConfigurationManager _config;
- public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager, ISessionManager sessionManager, IDeviceManager deviceManager)
+ public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager, ISessionManager sessionManager, INetworkManager networkManager)
{
AuthorizationContext = authorizationContext;
_config = config;
- DeviceManager = deviceManager;
SessionManager = sessionManager;
ConnectManager = connectManager;
UserManager = userManager;
+ NetworkManager = networkManager;
}
public IUserManager UserManager { get; private set; }
public IAuthorizationContext AuthorizationContext { get; private set; }
public IConnectManager ConnectManager { get; private set; }
public ISessionManager SessionManager { get; private set; }
- public IDeviceManager DeviceManager { get; private set; }
+ public INetworkManager NetworkManager { get; private set; }
/// <summary>
/// Redirect the client to a specific URL if authentication failed.
@@ -38,14 +39,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
/// </summary>
public string HtmlRedirect { get; set; }
- public void Authenticate(IRequest request,
- IAuthenticationAttributes authAttribtues)
+ public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
{
ValidateUser(request, authAttribtues);
}
- private void ValidateUser(IRequest request,
- IAuthenticationAttributes authAttribtues)
+ private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
{
// This code is executed before the service
var auth = AuthorizationContext.GetAuthorizationInfo(request);
@@ -60,11 +59,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
}
- var user = string.IsNullOrWhiteSpace(auth.UserId)
- ? null
- : UserManager.GetUserById(auth.UserId);
+ if (authAttribtues.AllowLocalOnly && !request.IsLocal)
+ {
+ throw new SecurityException("Operation not found.");
+ }
- if (user == null & !string.IsNullOrWhiteSpace(auth.UserId))
+ var user = auth.User;
+
+ if (user == null & !auth.UserId.Equals(Guid.Empty))
{
throw new SecurityException("User with Id " + auth.UserId + " not found");
}
@@ -83,9 +85,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
ValidateRoles(roles, user);
}
- if (!string.IsNullOrWhiteSpace(auth.DeviceId) &&
- !string.IsNullOrWhiteSpace(auth.Client) &&
- !string.IsNullOrWhiteSpace(auth.Device))
+ if (!string.IsNullOrEmpty(auth.DeviceId) &&
+ !string.IsNullOrEmpty(auth.Client) &&
+ !string.IsNullOrEmpty(auth.Device))
{
SessionManager.LogSessionActivity(auth.Client,
auth.Version,
@@ -108,6 +110,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
};
}
+ if (!user.Policy.EnableRemoteAccess && !NetworkManager.IsInLocalNetwork(request.RemoteIp))
+ {
+ throw new SecurityException("User account has been disabled.")
+ {
+ SecurityExceptionType = SecurityExceptionType.Unauthenticated
+ };
+ }
+
if (!user.Policy.IsAdministrator &&
!authAttribtues.EscapeParentalControl &&
!user.IsParentalScheduleAllowed())
@@ -119,17 +129,6 @@ namespace Emby.Server.Implementations.HttpServer.Security
SecurityExceptionType = SecurityExceptionType.ParentalControl
};
}
-
- if (!string.IsNullOrWhiteSpace(auth.DeviceId))
- {
- if (!DeviceManager.CanAccessDevice(user.Id.ToString("N"), auth.DeviceId))
- {
- throw new SecurityException("User is not allowed access from this device.")
- {
- SecurityExceptionType = SecurityExceptionType.ParentalControl
- };
- }
- }
}
private bool IsExemptFromAuthenticationToken(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, IRequest request)
@@ -143,6 +142,10 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
return true;
}
+ if (authAttribtues.AllowLocalOnly && request.IsLocal)
+ {
+ return true;
+ }
return false;
}
@@ -159,12 +162,17 @@ namespace Emby.Server.Implementations.HttpServer.Security
return true;
}
- if (string.IsNullOrWhiteSpace(auth.Token))
+ if (authAttribtues.AllowLocalOnly && request.IsLocal)
+ {
+ return true;
+ }
+
+ if (string.IsNullOrEmpty(auth.Token))
{
return true;
}
- if (tokenInfo != null && string.IsNullOrWhiteSpace(tokenInfo.UserId))
+ if (tokenInfo != null && tokenInfo.UserId.Equals(Guid.Empty))
{
return true;
}
@@ -225,7 +233,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateSecurityToken(IRequest request, string token)
{
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
throw new SecurityException("Access token is required.");
}
@@ -237,12 +245,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
throw new SecurityException("Access token is invalid or expired.");
}
- if (!info.IsActive)
- {
- throw new SecurityException("Access token has expired.");
- }
-
- //if (!string.IsNullOrWhiteSpace(info.UserId))
+ //if (!string.IsNullOrEmpty(info.UserId))
//{
// var user = _userManager.GetUserById(info.UserId);
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index a41c51d1a..ee8f6c60b 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using MediaBrowser.Model.Services;
using System.Linq;
using System.Threading;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.HttpServer.Security
{
@@ -13,11 +14,13 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
private readonly IAuthenticationRepository _authRepo;
private readonly IConnectManager _connectManager;
+ private readonly IUserManager _userManager;
- public AuthorizationContext(IAuthenticationRepository authRepo, IConnectManager connectManager)
+ public AuthorizationContext(IAuthenticationRepository authRepo, IConnectManager connectManager, IUserManager userManager)
{
_authRepo = authRepo;
_connectManager = connectManager;
+ _userManager = userManager;
}
public AuthorizationInfo GetAuthorizationInfo(object requestContext)
@@ -60,16 +63,16 @@ namespace Emby.Server.Implementations.HttpServer.Security
auth.TryGetValue("Token", out token);
}
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
token = httpReq.Headers["X-Emby-Token"];
}
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
token = httpReq.Headers["X-MediaBrowser-Token"];
}
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
token = httpReq.QueryString["api_key"];
}
@@ -94,8 +97,6 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (tokenInfo != null)
{
- info.UserId = tokenInfo.UserId;
-
var updateToken = false;
// TODO: Remove these checks for IsNullOrWhiteSpace
@@ -109,15 +110,21 @@ namespace Emby.Server.Implementations.HttpServer.Security
info.DeviceId = tokenInfo.DeviceId;
}
+ // Temporary. TODO - allow clients to specify that the token has been shared with a casting device
+ var allowTokenInfoUpdate = info.Client == null || info.Client.IndexOf("chromecast", StringComparison.OrdinalIgnoreCase) == -1;
if (string.IsNullOrWhiteSpace(info.Device))
{
info.Device = tokenInfo.DeviceName;
}
+
else if (!string.Equals(info.Device, tokenInfo.DeviceName, StringComparison.OrdinalIgnoreCase))
{
- updateToken = true;
- tokenInfo.DeviceName = info.Device;
+ if (allowTokenInfoUpdate)
+ {
+ updateToken = true;
+ tokenInfo.DeviceName = info.Device;
+ }
}
if (string.IsNullOrWhiteSpace(info.Version))
@@ -126,22 +133,38 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
else if (!string.Equals(info.Version, tokenInfo.AppVersion, StringComparison.OrdinalIgnoreCase))
{
+ if (allowTokenInfoUpdate)
+ {
+ updateToken = true;
+ tokenInfo.AppVersion = info.Version;
+ }
+ }
+
+ if ((DateTime.UtcNow - tokenInfo.DateLastActivity).TotalMinutes > 3)
+ {
+ tokenInfo.DateLastActivity = DateTime.UtcNow;
updateToken = true;
- tokenInfo.AppVersion = info.Version;
+ }
+
+ if (!tokenInfo.UserId.Equals(Guid.Empty))
+ {
+ info.User = _userManager.GetUserById(tokenInfo.UserId);
+
+ if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
+ {
+ tokenInfo.UserName = info.User.Name;
+ updateToken = true;
+ }
}
if (updateToken)
{
- _authRepo.Update(tokenInfo, CancellationToken.None);
+ _authRepo.Update(tokenInfo);
}
}
else
{
- var user = _connectManager.GetUserFromExchangeToken(token);
- if (user != null)
- {
- info.UserId = user.Id.ToString("N");
- }
+ info.User = _connectManager.GetUserFromExchangeToken(token);
}
httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo;
}
@@ -160,7 +183,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
var auth = httpReq.Headers["X-Emby-Authorization"];
- if (string.IsNullOrWhiteSpace(auth))
+ if (string.IsNullOrEmpty(auth))
{
auth = httpReq.Headers["Authorization"];
}
@@ -212,7 +235,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private string NormalizeValue(string value)
{
- if (string.IsNullOrWhiteSpace(value))
+ if (string.IsNullOrEmpty(value))
{
return value;
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 9826a0d56..a919ce008 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using System;
namespace Emby.Server.Implementations.HttpServer.Security
{
@@ -21,11 +22,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
_sessionManager = sessionManager;
}
- public Task<SessionInfo> GetSession(IRequest requestContext)
+ public SessionInfo GetSession(IRequest requestContext)
{
var authorization = _authContext.GetAuthorizationInfo(requestContext);
- var user = string.IsNullOrWhiteSpace(authorization.UserId) ? null : _userManager.GetUserById(authorization.UserId);
+ var user = authorization.User;
return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.RemoteIp, user);
}
@@ -36,19 +37,19 @@ namespace Emby.Server.Implementations.HttpServer.Security
return info as AuthenticationInfo;
}
- public Task<SessionInfo> GetSession(object requestContext)
+ public SessionInfo GetSession(object requestContext)
{
return GetSession((IRequest)requestContext);
}
- public async Task<User> GetUser(IRequest requestContext)
+ public User GetUser(IRequest requestContext)
{
- var session = await GetSession(requestContext).ConfigureAwait(false);
+ var session = GetSession(requestContext);
- return session == null || !session.UserId.HasValue ? null : _userManager.GetUserById(session.UserId.Value);
+ return session == null || session.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(session.UserId);
}
- public Task<User> GetUser(object requestContext)
+ public User GetUser(object requestContext)
{
return GetUser((IRequest)requestContext);
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
deleted file mode 100644
index 07a338f19..000000000
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using SocketHttpListener.Net;
-
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
-{
- public static class Extensions
- {
- public static string GetOperationName(this HttpListenerRequest request)
- {
- return request.Url.Segments[request.Url.Segments.Length - 1];
- }
- }
-}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
deleted file mode 100644
index 4e8dd7362..000000000
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
+++ /dev/null
@@ -1,923 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.Extensions;
-
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
-{
- public static class MyHttpUtility
- {
- // Must be sorted
- static readonly long[] entities = new long[] {
- (long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
- (long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
- (long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
- (long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
- (long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40,
- (long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
- (long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
- (long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40,
- (long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40,
- (long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
- (long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
- (long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
- (long)'M' << 56 | (long)'u' << 48,
- (long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'N' << 56 | (long)'u' << 48,
- (long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
- (long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
- (long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
- (long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40,
- (long)'P' << 56 | (long)'i' << 48,
- (long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
- (long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40,
- (long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40,
- (long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
- (long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
- (long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24,
- (long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40,
- (long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
- (long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'X' << 56 | (long)'i' << 48,
- (long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24,
- (long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
- (long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8,
- (long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
- (long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40,
- (long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40,
- (long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40,
- (long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32,
- (long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
- (long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24,
- (long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16,
- (long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32,
- (long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40,
- (long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
- (long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24,
- (long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32,
- (long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40,
- (long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32,
- (long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24,
- (long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32,
- (long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32,
- (long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24,
- (long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40,
- (long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16,
- (long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
- (long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40,
- (long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
- (long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24,
- (long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24,
- (long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32,
- (long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32,
- (long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24,
- (long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40,
- (long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40,
- (long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32,
- (long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24,
- (long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32,
- (long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16,
- (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16,
- (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16,
- (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16,
- (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24,
- (long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
- (long)'g' << 56 | (long)'e' << 48,
- (long)'g' << 56 | (long)'t' << 48,
- (long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16,
- (long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16,
- (long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24,
- (long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24,
- (long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24,
- (long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40,
- (long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16,
- (long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32,
- (long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
- (long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
- (long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
- (long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
- (long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'l' << 56 | (long)'e' << 48,
- (long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
- (long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16,
- (long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40,
- (long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40,
- (long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
- (long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'l' << 56 | (long)'t' << 48,
- (long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32,
- (long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
- (long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24,
- (long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16,
- (long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24,
- (long)'m' << 56 | (long)'u' << 48,
- (long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24,
- (long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32,
- (long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
- (long)'n' << 56 | (long)'e' << 48,
- (long)'n' << 56 | (long)'i' << 48,
- (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40,
- (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24,
- (long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32,
- (long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'n' << 56 | (long)'u' << 48,
- (long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
- (long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24,
- (long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
- (long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24,
- (long)'o' << 56 | (long)'r' << 48,
- (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32,
- (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32,
- (long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
- (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
- (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16,
- (long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32,
- (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32,
- (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16,
- (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32,
- (long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40,
- (long)'p' << 56 | (long)'i' << 48,
- (long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40,
- (long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16,
- (long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24,
- (long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
- (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32,
- (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32,
- (long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40,
- (long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32,
- (long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24,
- (long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
- (long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
- (long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32,
- (long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40,
- (long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
- (long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40,
- (long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40,
- (long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
- (long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
- (long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
- (long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32,
- (long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32,
- (long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40,
- (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
- (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16,
- (long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40,
- (long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16,
- (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40,
- (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32,
- (long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40,
- (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40,
- (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32,
- (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32,
- (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32,
- (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32,
- (long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
- (long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40,
- (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16,
- (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
- (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0,
- (long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16,
- (long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24,
- (long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24,
- (long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24,
- (long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24,
- (long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
- (long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
- (long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
- (long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40,
- (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24,
- (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
- (long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16,
- (long)'x' << 56 | (long)'i' << 48,
- (long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
- (long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40,
- (long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
- (long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
- (long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40,
- (long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32
- };
-
- static readonly char[] entities_values = new char[] {
- '\u00C6',
- '\u00C1',
- '\u00C2',
- '\u00C0',
- '\u0391',
- '\u00C5',
- '\u00C3',
- '\u00C4',
- '\u0392',
- '\u00C7',
- '\u03A7',
- '\u2021',
- '\u0394',
- '\u00D0',
- '\u00C9',
- '\u00CA',
- '\u00C8',
- '\u0395',
- '\u0397',
- '\u00CB',
- '\u0393',
- '\u00CD',
- '\u00CE',
- '\u00CC',
- '\u0399',
- '\u00CF',
- '\u039A',
- '\u039B',
- '\u039C',
- '\u00D1',
- '\u039D',
- '\u0152',
- '\u00D3',
- '\u00D4',
- '\u00D2',
- '\u03A9',
- '\u039F',
- '\u00D8',
- '\u00D5',
- '\u00D6',
- '\u03A6',
- '\u03A0',
- '\u2033',
- '\u03A8',
- '\u03A1',
- '\u0160',
- '\u03A3',
- '\u00DE',
- '\u03A4',
- '\u0398',
- '\u00DA',
- '\u00DB',
- '\u00D9',
- '\u03A5',
- '\u00DC',
- '\u039E',
- '\u00DD',
- '\u0178',
- '\u0396',
- '\u00E1',
- '\u00E2',
- '\u00B4',
- '\u00E6',
- '\u00E0',
- '\u2135',
- '\u03B1',
- '\u0026',
- '\u2227',
- '\u2220',
- '\u0027',
- '\u00E5',
- '\u2248',
- '\u00E3',
- '\u00E4',
- '\u201E',
- '\u03B2',
- '\u00A6',
- '\u2022',
- '\u2229',
- '\u00E7',
- '\u00B8',
- '\u00A2',
- '\u03C7',
- '\u02C6',
- '\u2663',
- '\u2245',
- '\u00A9',
- '\u21B5',
- '\u222A',
- '\u00A4',
- '\u21D3',
- '\u2020',
- '\u2193',
- '\u00B0',
- '\u03B4',
- '\u2666',
- '\u00F7',
- '\u00E9',
- '\u00EA',
- '\u00E8',
- '\u2205',
- '\u2003',
- '\u2002',
- '\u03B5',
- '\u2261',
- '\u03B7',
- '\u00F0',
- '\u00EB',
- '\u20AC',
- '\u2203',
- '\u0192',
- '\u2200',
- '\u00BD',
- '\u00BC',
- '\u00BE',
- '\u2044',
- '\u03B3',
- '\u2265',
- '\u003E',
- '\u21D4',
- '\u2194',
- '\u2665',
- '\u2026',
- '\u00ED',
- '\u00EE',
- '\u00A1',
- '\u00EC',
- '\u2111',
- '\u221E',
- '\u222B',
- '\u03B9',
- '\u00BF',
- '\u2208',
- '\u00EF',
- '\u03BA',
- '\u21D0',
- '\u03BB',
- '\u2329',
- '\u00AB',
- '\u2190',
- '\u2308',
- '\u201C',
- '\u2264',
- '\u230A',
- '\u2217',
- '\u25CA',
- '\u200E',
- '\u2039',
- '\u2018',
- '\u003C',
- '\u00AF',
- '\u2014',
- '\u00B5',
- '\u00B7',
- '\u2212',
- '\u03BC',
- '\u2207',
- '\u00A0',
- '\u2013',
- '\u2260',
- '\u220B',
- '\u00AC',
- '\u2209',
- '\u2284',
- '\u00F1',
- '\u03BD',
- '\u00F3',
- '\u00F4',
- '\u0153',
- '\u00F2',
- '\u203E',
- '\u03C9',
- '\u03BF',
- '\u2295',
- '\u2228',
- '\u00AA',
- '\u00BA',
- '\u00F8',
- '\u00F5',
- '\u2297',
- '\u00F6',
- '\u00B6',
- '\u2202',
- '\u2030',
- '\u22A5',
- '\u03C6',
- '\u03C0',
- '\u03D6',
- '\u00B1',
- '\u00A3',
- '\u2032',
- '\u220F',
- '\u221D',
- '\u03C8',
- '\u0022',
- '\u21D2',
- '\u221A',
- '\u232A',
- '\u00BB',
- '\u2192',
- '\u2309',
- '\u201D',
- '\u211C',
- '\u00AE',
- '\u230B',
- '\u03C1',
- '\u200F',
- '\u203A',
- '\u2019',
- '\u201A',
- '\u0161',
- '\u22C5',
- '\u00A7',
- '\u00AD',
- '\u03C3',
- '\u03C2',
- '\u223C',
- '\u2660',
- '\u2282',
- '\u2286',
- '\u2211',
- '\u2283',
- '\u00B9',
- '\u00B2',
- '\u00B3',
- '\u2287',
- '\u00DF',
- '\u03C4',
- '\u2234',
- '\u03B8',
- '\u03D1',
- '\u2009',
- '\u00FE',
- '\u02DC',
- '\u00D7',
- '\u2122',
- '\u21D1',
- '\u00FA',
- '\u2191',
- '\u00FB',
- '\u00F9',
- '\u00A8',
- '\u03D2',
- '\u03C5',
- '\u00FC',
- '\u2118',
- '\u03BE',
- '\u00FD',
- '\u00A5',
- '\u00FF',
- '\u03B6',
- '\u200D',
- '\u200C'
- };
-
- #region Methods
-
- static void WriteCharBytes(IList buf, char ch, Encoding e)
- {
- if (ch > 255)
- {
- foreach (byte b in e.GetBytes(new char[] { ch }))
- buf.Add(b);
- }
- else
- buf.Add((byte)ch);
- }
-
- public static string UrlDecode(string s, Encoding e)
- {
- if (null == s)
- return null;
-
- if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
- return s;
-
- if (e == null)
- e = Encoding.UTF8;
-
- long len = s.Length;
- var bytes = new List<byte>();
- int xchar;
- char ch;
-
- for (int i = 0; i < len; i++)
- {
- ch = s[i];
- if (ch == '%' && i + 2 < len && s[i + 1] != '%')
- {
- if (s[i + 1] == 'u' && i + 5 < len)
- {
- // unicode hex sequence
- xchar = GetChar(s, i + 2, 4);
- if (xchar != -1)
- {
- WriteCharBytes(bytes, (char)xchar, e);
- i += 5;
- }
- else
- WriteCharBytes(bytes, '%', e);
- }
- else if ((xchar = GetChar(s, i + 1, 2)) != -1)
- {
- WriteCharBytes(bytes, (char)xchar, e);
- i += 2;
- }
- else
- {
- WriteCharBytes(bytes, '%', e);
- }
- continue;
- }
-
- if (ch == '+')
- WriteCharBytes(bytes, ' ', e);
- else
- WriteCharBytes(bytes, ch, e);
- }
-
- byte[] buf = bytes.ToArray(bytes.Count);
- bytes = null;
- return e.GetString(buf, 0, buf.Length);
-
- }
-
- static int GetInt(byte b)
- {
- char c = (char)b;
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -1;
- }
-
- static int GetChar(string str, int offset, int length)
- {
- int val = 0;
- int end = length + offset;
- for (int i = offset; i < end; i++)
- {
- char c = str[i];
- if (c > 127)
- return -1;
-
- int current = GetInt((byte)c);
- if (current == -1)
- return -1;
- val = (val << 4) + current;
- }
-
- return val;
- }
-
- static bool TryConvertKeyToEntity(string key, out char value)
- {
- var token = CalculateKeyValue(key);
- if (token == 0)
- {
- value = '\0';
- return false;
- }
-
- var idx = Array.BinarySearch(entities, token);
- if (idx < 0)
- {
- value = '\0';
- return false;
- }
-
- value = entities_values[idx];
- return true;
- }
-
- static long CalculateKeyValue(string s)
- {
- if (s.Length > 8)
- return 0;
-
- long key = 0;
- for (int i = 0; i < s.Length; ++i)
- {
- long ch = s[i];
- if (ch > 'z' || ch < '0')
- return 0;
-
- key |= ch << ((7 - i) * 8);
- }
-
- return key;
- }
-
- /// <summary>
- /// Decodes an HTML-encoded string and returns the decoded string.
- /// </summary>
- /// <param name="s">The HTML string to decode. </param>
- /// <returns>The decoded text.</returns>
- public static string HtmlDecode(string s)
- {
- if (s == null)
- throw new ArgumentNullException("s");
-
- if (s.IndexOf('&') == -1)
- return s;
-
- StringBuilder entity = new StringBuilder();
- StringBuilder output = new StringBuilder();
- int len = s.Length;
- // 0 -> nothing,
- // 1 -> right after '&'
- // 2 -> between '&' and ';' but no '#'
- // 3 -> '#' found after '&' and getting numbers
- int state = 0;
- int number = 0;
- int digit_start = 0;
- bool hex_number = false;
-
- for (int i = 0; i < len; i++)
- {
- char c = s[i];
- if (state == 0)
- {
- if (c == '&')
- {
- entity.Append(c);
- state = 1;
- }
- else
- {
- output.Append(c);
- }
- continue;
- }
-
- if (c == '&')
- {
- state = 1;
- if (digit_start > 0)
- {
- entity.Append(s, digit_start, i - digit_start);
- digit_start = 0;
- }
-
- output.Append(entity.ToString());
- entity.Length = 0;
- entity.Append('&');
- continue;
- }
-
- switch (state)
- {
- case 1:
- if (c == ';')
- {
- state = 0;
- output.Append(entity.ToString());
- output.Append(c);
- entity.Length = 0;
- break;
- }
-
- number = 0;
- hex_number = false;
- if (c != '#')
- {
- state = 2;
- }
- else
- {
- state = 3;
- }
- entity.Append(c);
-
- break;
- case 2:
- entity.Append(c);
- if (c == ';')
- {
- string key = entity.ToString();
- state = 0;
- entity.Length = 0;
-
- if (key.Length > 1)
- {
- var skey = key.Substring(1, key.Length - 2);
- if (TryConvertKeyToEntity(skey, out c))
- {
- output.Append(c);
- break;
- }
- }
-
- output.Append(key);
- }
-
- break;
- case 3:
- if (c == ';')
- {
- if (number < 0x10000)
- {
- output.Append((char)number);
- }
- else
- {
- output.Append((char)(0xd800 + ((number - 0x10000) >> 10)));
- output.Append((char)(0xdc00 + ((number - 0x10000) & 0x3ff)));
- }
- state = 0;
- entity.Length = 0;
- digit_start = 0;
- break;
- }
-
- if (c == 'x' || c == 'X' && !hex_number)
- {
- digit_start = i;
- hex_number = true;
- break;
- }
-
- if (Char.IsDigit(c))
- {
- if (digit_start == 0)
- digit_start = i;
-
- number = number * (hex_number ? 16 : 10) + ((int)c - '0');
- break;
- }
-
- if (hex_number)
- {
- if (c >= 'a' && c <= 'f')
- {
- number = number * 16 + 10 + ((int)c - 'a');
- break;
- }
- if (c >= 'A' && c <= 'F')
- {
- number = number * 16 + 10 + ((int)c - 'A');
- break;
- }
- }
-
- state = 2;
- if (digit_start > 0)
- {
- entity.Append(s, digit_start, i - digit_start);
- digit_start = 0;
- }
-
- entity.Append(c);
- break;
- }
- }
-
- if (entity.Length > 0)
- {
- output.Append(entity);
- }
- else if (digit_start > 0)
- {
- output.Append(s, digit_start, s.Length - digit_start);
- }
- return output.ToString();
- }
-
- public static QueryParamCollection ParseQueryString(string query)
- {
- return ParseQueryString(query, Encoding.UTF8);
- }
-
- public static QueryParamCollection ParseQueryString(string query, Encoding encoding)
- {
- if (query == null)
- throw new ArgumentNullException("query");
- if (encoding == null)
- throw new ArgumentNullException("encoding");
- if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
- return new QueryParamCollection();
- if (query[0] == '?')
- query = query.Substring(1);
-
- QueryParamCollection result = new QueryParamCollection();
- ParseQueryString(query, encoding, result);
- return result;
- }
-
- internal static void ParseQueryString(string query, Encoding encoding, QueryParamCollection result)
- {
- if (query.Length == 0)
- return;
-
- string decoded = HtmlDecode(query);
- int decodedLength = decoded.Length;
- int namePos = 0;
- bool first = true;
- while (namePos <= decodedLength)
- {
- int valuePos = -1, valueEnd = -1;
- for (int q = namePos; q < decodedLength; q++)
- {
- if (valuePos == -1 && decoded[q] == '=')
- {
- valuePos = q + 1;
- }
- else if (decoded[q] == '&')
- {
- valueEnd = q;
- break;
- }
- }
-
- if (first)
- {
- first = false;
- if (decoded[namePos] == '?')
- namePos++;
- }
-
- string name, value;
- if (valuePos == -1)
- {
- name = null;
- valuePos = namePos;
- }
- else
- {
- name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
- }
- if (valueEnd < 0)
- {
- namePos = -1;
- valueEnd = decoded.Length;
- }
- else
- {
- namePos = valueEnd + 1;
- }
- value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
-
- result.Add(name, value);
- if (namePos == -1)
- break;
- }
- }
- #endregion // Methods
- }
-}
diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
index 5d42f42fa..3f394d3ac 100644
--- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
@@ -73,7 +73,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
- public StreamWriter(byte[] source, string contentType, ILogger logger)
+ public StreamWriter(byte[] source, string contentType, int contentLength, ILogger logger)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -85,7 +85,7 @@ namespace Emby.Server.Implementations.HttpServer
Headers["Content-Type"] = contentType;
- Headers["Content-Length"] = source.Length.ToString(UsCulture);
+ Headers["Content-Length"] = contentLength.ToString(UsCulture);
}
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
@@ -106,10 +106,8 @@ namespace Emby.Server.Implementations.HttpServer
}
}
}
- catch (Exception ex)
+ catch
{
- Logger.ErrorException("Error streaming data", ex);
-
if (OnError != null)
{
OnError();
diff --git a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 076f50d93..d449e4424 100644
--- a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -5,15 +5,14 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using System;
-using System.Collections.Specialized;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Text;
+using System.Net.WebSockets;
+using Emby.Server.Implementations.Net;
-namespace Emby.Server.Implementations.ServerManager
+namespace Emby.Server.Implementations.HttpServer
{
/// <summary>
/// Class WebSocketConnection
@@ -33,11 +32,6 @@ namespace Emby.Server.Implementations.ServerManager
public string RemoteEndPoint { get; private set; }
/// <summary>
- /// The _cancellation token source
- /// </summary>
- private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
-
- /// <summary>
/// The logger
/// </summary>
private readonly ILogger _logger;
@@ -51,7 +45,7 @@ namespace Emby.Server.Implementations.ServerManager
/// Gets or sets the receive action.
/// </summary>
/// <value>The receive action.</value>
- public Action<WebSocketMessageInfo> OnReceive { get; set; }
+ public Func<WebSocketMessageInfo, Task> OnReceive { get; set; }
/// <summary>
/// Gets the last activity date.
@@ -75,7 +69,6 @@ namespace Emby.Server.Implementations.ServerManager
/// </summary>
/// <value>The query string.</value>
public QueryParamCollection QueryString { get; set; }
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly ITextEncoding _textEncoding;
/// <summary>
@@ -86,7 +79,7 @@ namespace Emby.Server.Implementations.ServerManager
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="logger">The logger.</param>
/// <exception cref="System.ArgumentNullException">socket</exception>
- public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding)
+ public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, ITextEncoding textEncoding)
{
if (socket == null)
{
@@ -109,10 +102,15 @@ namespace Emby.Server.Implementations.ServerManager
_jsonSerializer = jsonSerializer;
_socket = socket;
_socket.OnReceiveBytes = OnReceiveInternal;
- _socket.OnReceive = OnReceiveInternal;
+
+ var memorySocket = socket as IMemoryWebSocket;
+ if (memorySocket != null)
+ {
+ memorySocket.OnReceiveMemoryBytes = OnReceiveInternal;
+ }
+
RemoteEndPoint = remoteEndPoint;
_logger = logger;
- _memoryStreamProvider = memoryStreamProvider;
_textEncoding = textEncoding;
socket.Closed += socket_Closed;
@@ -148,6 +146,33 @@ namespace Emby.Server.Implementations.ServerManager
}
}
+ /// <summary>
+ /// Called when [receive].
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ private void OnReceiveInternal(Memory<byte> memory, int length)
+ {
+ LastActivityDate = DateTime.UtcNow;
+
+ if (OnReceive == null)
+ {
+ return;
+ }
+
+ var bytes = memory.Slice(0, length).ToArray();
+
+ var charset = _textEncoding.GetDetectedEncodingName(bytes, bytes.Length, null, false);
+
+ if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
+ {
+ OnReceiveInternal(Encoding.UTF8.GetString(bytes, 0, bytes.Length));
+ }
+ else
+ {
+ OnReceiveInternal(_textEncoding.GetASCIIEncoding().GetString(bytes, 0, bytes.Length));
+ }
+ }
+
private void OnReceiveInternal(string message)
{
LastActivityDate = DateTime.UtcNow;
@@ -223,7 +248,7 @@ namespace Emby.Server.Implementations.ServerManager
public Task SendAsync(string text, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(text))
+ if (string.IsNullOrEmpty(text))
{
throw new ArgumentNullException("text");
}
@@ -248,7 +273,6 @@ namespace Emby.Server.Implementations.ServerManager
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -259,7 +283,6 @@ namespace Emby.Server.Implementations.ServerManager
{
if (dispose)
{
- _cancellationTokenSource.Dispose();
_socket.Dispose();
}
}
diff --git a/Emby.Server.Implementations/HttpServerFactory.cs b/Emby.Server.Implementations/HttpServerFactory.cs
deleted file mode 100644
index 717c50e7b..000000000
--- a/Emby.Server.Implementations/HttpServerFactory.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using System.IO;
-using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-using Emby.Server.Implementations.Net;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.System;
-using MediaBrowser.Model.Text;
-using ServiceStack.Text.Jsv;
-using SocketHttpListener.Primitives;
-
-namespace Emby.Server.Implementations
-{
- /// <summary>
- /// Class ServerFactory
- /// </summary>
- public static class HttpServerFactory
- {
- /// <summary>
- /// Creates the server.
- /// </summary>
- /// <returns>IHttpServer.</returns>
- public static IHttpServer CreateServer(IServerApplicationHost applicationHost,
- ILogManager logManager,
- IServerConfigurationManager config,
- INetworkManager networkmanager,
- IMemoryStreamFactory streamProvider,
- string serverName,
- string defaultRedirectpath,
- ITextEncoding textEncoding,
- ISocketFactory socketFactory,
- ICryptoProvider cryptoProvider,
- IJsonSerializer json,
- IXmlSerializer xml,
- IEnvironmentInfo environment,
- X509Certificate certificate,
- IFileSystem fileSystem,
- bool enableDualModeSockets)
- {
- var logger = logManager.GetLogger("HttpServer");
-
- return new HttpListenerHost(applicationHost,
- logger,
- config,
- serverName,
- defaultRedirectpath,
- networkmanager,
- streamProvider,
- textEncoding,
- socketFactory,
- cryptoProvider,
- json,
- xml,
- environment,
- certificate,
- GetParseFn,
- enableDualModeSockets,
- fileSystem);
- }
-
- private static Func<string, object> GetParseFn(Type propertyType)
- {
- return s => JsvReader.GetParseFn(propertyType)(s);
- }
- }
-}
diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
new file mode 100644
index 000000000..6b08c26c9
--- /dev/null
+++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Emby.Server.Implementations.IO
+{
+ public class ExtendedFileSystemInfo
+ {
+ public bool IsHidden { get; set; }
+ public bool IsReadOnly { get; set; }
+ public bool Exists { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 85b8bddd2..4be30f8b7 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.IO
private void AddAffectedPath(string path)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.IO
public void AddPath(string path)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
@@ -113,7 +113,7 @@ namespace Emby.Server.Implementations.IO
Path = path;
AddAffectedPath(path);
- if (!string.IsNullOrWhiteSpace(affectedFile))
+ if (!string.IsNullOrEmpty(affectedFile))
{
AddAffectedPath(affectedFile);
}
@@ -202,7 +202,7 @@ namespace Emby.Server.Implementations.IO
// If the item has been deleted find the first valid parent that still exists
while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
{
- item = item.IsOwnedItem ? item.GetOwner() : item.GetParent();
+ item = item.GetOwner() ?? item.GetParent();
if (item == null)
{
@@ -231,7 +231,6 @@ namespace Emby.Server.Implementations.IO
{
_disposed = true;
DisposeTimer();
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/IO/IsoManager.cs b/Emby.Server.Implementations/IO/IsoManager.cs
index dc0b9e122..903d5f301 100644
--- a/Emby.Server.Implementations/IO/IsoManager.cs
+++ b/Emby.Server.Implementations/IO/IsoManager.cs
@@ -70,7 +70,6 @@ namespace Emby.Server.Implementations.IO
{
mounter.Dispose();
}
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index a2abb2a5c..00fe447f0 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -266,7 +266,7 @@ namespace Emby.Server.Implementations.IO
/// <exception cref="System.ArgumentNullException">path</exception>
private static bool ContainsParentFolder(IEnumerable<string> lst, string path)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
@@ -304,6 +304,12 @@ namespace Emby.Server.Implementations.IO
}
}
+ if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Android)
+ {
+ // causing crashing
+ return;
+ }
+
// Already being watched
if (_fileSystemWatchers.ContainsKey(path))
{
@@ -320,11 +326,7 @@ namespace Emby.Server.Implementations.IO
IncludeSubdirectories = true
};
- if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows ||
- _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
- {
- newWatcher.InternalBufferSize = 32767;
- }
+ newWatcher.InternalBufferSize = 65536;
newWatcher.NotifyFilter = NotifyFilters.CreationTime |
NotifyFilters.DirectoryName |
@@ -337,7 +339,6 @@ namespace Emby.Server.Implementations.IO
newWatcher.Deleted += watcher_Changed;
newWatcher.Renamed += watcher_Changed;
newWatcher.Changed += watcher_Changed;
-
newWatcher.Error += watcher_Error;
if (_fileSystemWatchers.TryAdd(path, newWatcher))
@@ -347,7 +348,7 @@ namespace Emby.Server.Implementations.IO
}
else
{
- newWatcher.Dispose();
+ DisposeWatcher(newWatcher, false);
}
}
@@ -368,15 +369,14 @@ namespace Emby.Server.Implementations.IO
if (_fileSystemWatchers.TryGetValue(path, out watcher))
{
- DisposeWatcher(watcher);
+ DisposeWatcher(watcher, true);
}
}
/// <summary>
/// Disposes the watcher.
/// </summary>
- /// <param name="watcher">The watcher.</param>
- private void DisposeWatcher(FileSystemWatcher watcher)
+ private void DisposeWatcher(FileSystemWatcher watcher, bool removeFromList)
{
try
{
@@ -384,16 +384,37 @@ namespace Emby.Server.Implementations.IO
{
Logger.Info("Stopping directory watching for path {0}", watcher.Path);
- watcher.EnableRaisingEvents = false;
+ watcher.Created -= watcher_Changed;
+ watcher.Deleted -= watcher_Changed;
+ watcher.Renamed -= watcher_Changed;
+ watcher.Changed -= watcher_Changed;
+ watcher.Error -= watcher_Error;
+
+ try
+ {
+ watcher.EnableRaisingEvents = false;
+ }
+ catch (InvalidOperationException)
+ {
+ // Seeing this under mono on linux sometimes
+ // Collection was modified; enumeration operation may not execute.
+ }
}
}
+ catch (NotImplementedException)
+ {
+ // the dispose method on FileSystemWatcher is sometimes throwing NotImplementedException on Xamarin Android
+ }
catch
{
}
finally
{
- RemoveWatcherFromList(watcher);
+ if (removeFromList)
+ {
+ RemoveWatcherFromList(watcher);
+ }
}
}
@@ -420,7 +441,7 @@ namespace Emby.Server.Implementations.IO
Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex);
- DisposeWatcher(dw);
+ DisposeWatcher(dw, true);
}
/// <summary>
@@ -452,7 +473,7 @@ namespace Emby.Server.Implementations.IO
}
var filename = Path.GetFileName(path);
-
+
var monitorPath = !string.IsNullOrEmpty(filename) &&
!_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
@@ -466,13 +487,13 @@ namespace Emby.Server.Implementations.IO
{
if (_fileSystem.AreEqual(i, path))
{
- Logger.Debug("Ignoring change to {0}", path);
+ //Logger.Debug("Ignoring change to {0}", path);
return true;
}
if (_fileSystem.ContainsSubPath(i, path))
{
- Logger.Debug("Ignoring change to {0}", path);
+ //Logger.Debug("Ignoring change to {0}", path);
return true;
}
@@ -482,7 +503,7 @@ namespace Emby.Server.Implementations.IO
{
if (_fileSystem.AreEqual(parent, path))
{
- Logger.Debug("Ignoring change to {0}", path);
+ //Logger.Debug("Ignoring change to {0}", path);
return true;
}
}
@@ -561,22 +582,7 @@ namespace Emby.Server.Implementations.IO
foreach (var watcher in _fileSystemWatchers.Values.ToList())
{
- watcher.Created -= watcher_Changed;
- watcher.Deleted -= watcher_Changed;
- watcher.Renamed -= watcher_Changed;
- watcher.Changed -= watcher_Changed;
-
- try
- {
- watcher.EnableRaisingEvents = false;
- }
- catch (InvalidOperationException)
- {
- // Seeing this under mono on linux sometimes
- // Collection was modified; enumeration operation may not execute.
- }
-
- watcher.Dispose();
+ DisposeWatcher(watcher, false);
}
_fileSystemWatchers.Clear();
@@ -612,7 +618,6 @@ namespace Emby.Server.Implementations.IO
{
_disposed = true;
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -644,7 +649,6 @@ namespace Emby.Server.Implementations.IO
public void Dispose()
{
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index c8e4031a9..66d7802c6 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -20,27 +20,57 @@ namespace Emby.Server.Implementations.IO
private readonly bool _supportsAsyncFileStreams;
private char[] _invalidFileNameChars;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
- private bool EnableFileSystemRequestConcat;
+ private bool EnableSeparateFileAndDirectoryQueries;
private string _tempPath;
private SharpCifsFileSystem _sharpCifsFileSystem;
private IEnvironmentInfo _environmentInfo;
+ private bool _isEnvironmentCaseInsensitive;
- public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string tempPath)
+ private string _defaultDirectory;
+
+ public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string defaultDirectory, string tempPath, bool enableSeparateFileAndDirectoryQueries)
{
Logger = logger;
_supportsAsyncFileStreams = true;
_tempPath = tempPath;
_environmentInfo = environmentInfo;
+ _defaultDirectory = defaultDirectory;
- // On Linux, this needs to be true or symbolic links are ignored
- EnableFileSystemRequestConcat = environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows &&
- environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX;
+ // On Linux with mono, this needs to be true or symbolic links are ignored
+ EnableSeparateFileAndDirectoryQueries = enableSeparateFileAndDirectoryQueries;
SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
_sharpCifsFileSystem = new SharpCifsFileSystem(environmentInfo.OperatingSystem);
+
+ _isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
+ }
+
+ public string DefaultDirectory
+ {
+ get
+ {
+ var value = _defaultDirectory;
+
+ if (!string.IsNullOrEmpty(value))
+ {
+ try
+ {
+ if (DirectoryExists(value))
+ {
+ return value;
+ }
+ }
+ catch
+ {
+
+ }
+ }
+
+ return null;
+ }
}
public void AddShortcutHandler(IShortcutHandler handler)
@@ -56,11 +86,9 @@ namespace Emby.Server.Implementations.IO
}
else
{
- // GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac.
- _invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
- '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
- '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
- '\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' };
+ // Be consistent across platforms because the windows server will fail to query network shares that don't follow windows conventions
+ // https://referencesource.microsoft.com/#mscorlib/system/io/path.cs
+ _invalidFileNameChars = new char[] { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31, ':', '*', '?', '\\', '/' };
}
}
@@ -118,6 +146,49 @@ namespace Emby.Server.Implementations.IO
return null;
}
+ public string MakeAbsolutePath(string folderPath, string filePath)
+ {
+ if (String.IsNullOrWhiteSpace(filePath)) return filePath;
+
+ if (filePath.Contains(@"://")) return filePath; //stream
+ if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/') return filePath; //absolute local path
+
+ // unc path
+ if (filePath.StartsWith("\\\\"))
+ {
+ return filePath;
+ }
+
+ var firstChar = filePath[0];
+ if (firstChar == '/')
+ {
+ // For this we don't really know.
+ return filePath;
+ }
+ if (firstChar == '\\') //relative path
+ {
+ filePath = filePath.Substring(1);
+ }
+ try
+ {
+ string path = System.IO.Path.Combine(folderPath, filePath);
+ path = System.IO.Path.GetFullPath(path);
+ return path;
+ }
+ catch (ArgumentException ex)
+ {
+ return filePath;
+ }
+ catch (PathTooLongException)
+ {
+ return filePath;
+ }
+ catch (NotSupportedException)
+ {
+ return filePath;
+ }
+ }
+
/// <summary>
/// Creates the shortcut.
/// </summary>
@@ -162,11 +233,6 @@ namespace Emby.Server.Implementations.IO
/// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks>
public FileSystemMetadata GetFileSystemInfo(string path)
{
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
if (_sharpCifsFileSystem.IsEnabledForPath(path))
{
return _sharpCifsFileSystem.GetFileSystemInfo(path);
@@ -207,11 +273,6 @@ namespace Emby.Server.Implementations.IO
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
public FileSystemMetadata GetFileInfo(string path)
{
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
if (_sharpCifsFileSystem.IsEnabledForPath(path))
{
return _sharpCifsFileSystem.GetFileInfo(path);
@@ -232,11 +293,6 @@ namespace Emby.Server.Implementations.IO
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
public FileSystemMetadata GetDirectoryInfo(string path)
{
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
if (_sharpCifsFileSystem.IsEnabledForPath(path))
{
return _sharpCifsFileSystem.GetDirectoryInfo(path);
@@ -258,10 +314,12 @@ namespace Emby.Server.Implementations.IO
if (result.Exists)
{
- var attributes = info.Attributes;
- result.IsDirectory = info is DirectoryInfo || (attributes & FileAttributes.Directory) == FileAttributes.Directory;
- result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
- result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
+ result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
+
+ //if (!result.IsDirectory)
+ //{
+ // result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
+ //}
var fileInfo = info as FileInfo;
if (fileInfo != null)
@@ -281,6 +339,25 @@ namespace Emby.Server.Implementations.IO
return result;
}
+ private ExtendedFileSystemInfo GetExtendedFileSystemInfo(string path)
+ {
+ var result = new ExtendedFileSystemInfo();
+
+ var info = new FileInfo(path);
+
+ if (info.Exists)
+ {
+ result.Exists = true;
+
+ var attributes = info.Attributes;
+
+ result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
+ result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
+ }
+
+ return result;
+ }
+
/// <summary>
/// The space char
/// </summary>
@@ -294,11 +371,6 @@ namespace Emby.Server.Implementations.IO
/// <exception cref="System.ArgumentNullException">filename</exception>
public string GetValidFilename(string filename)
{
- if (string.IsNullOrEmpty(filename))
- {
- throw new ArgumentNullException("filename");
- }
-
var builder = new StringBuilder(filename);
foreach (var c in _invalidFileNameChars)
@@ -484,7 +556,7 @@ namespace Emby.Server.Implementations.IO
return;
}
- var info = GetFileInfo(path);
+ var info = GetExtendedFileSystemInfo(path);
if (info.Exists && info.IsHidden != isHidden)
{
@@ -514,7 +586,7 @@ namespace Emby.Server.Implementations.IO
return;
}
- var info = GetFileInfo(path);
+ var info = GetExtendedFileSystemInfo(path);
if (info.Exists && info.IsReadOnly != isReadOnly)
{
@@ -544,7 +616,7 @@ namespace Emby.Server.Implementations.IO
return;
}
- var info = GetFileInfo(path);
+ var info = GetExtendedFileSystemInfo(path);
if (!info.Exists)
{
@@ -720,11 +792,6 @@ namespace Emby.Server.Implementations.IO
public bool IsPathFile(string path)
{
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
// Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
if (_sharpCifsFileSystem.IsEnabledForPath(path))
@@ -822,7 +889,7 @@ namespace Emby.Server.Implementations.IO
// On linux and osx the search pattern is case sensitive
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
- if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1)
+ if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1)
{
return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], searchOption));
}
@@ -855,7 +922,7 @@ namespace Emby.Server.Implementations.IO
var directoryInfo = new DirectoryInfo(path);
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
- if (EnableFileSystemRequestConcat)
+ if (EnableSeparateFileAndDirectoryQueries)
{
return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
.Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
@@ -897,9 +964,28 @@ namespace Emby.Server.Implementations.IO
return File.OpenRead(path);
}
+ private void CopyFileUsingStreams(string source, string target, bool overwrite)
+ {
+ using (var sourceStream = OpenRead(source))
+ {
+ using (var targetStream = GetFileStream(target, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ {
+ sourceStream.CopyTo(targetStream);
+ }
+ }
+ }
+
public void CopyFile(string source, string target, bool overwrite)
{
- if (_sharpCifsFileSystem.IsEnabledForPath(source))
+ var enableSharpCifsForSource = _sharpCifsFileSystem.IsEnabledForPath(source);
+
+ if (enableSharpCifsForSource != _sharpCifsFileSystem.IsEnabledForPath(target))
+ {
+ CopyFileUsingStreams(source, target, overwrite);
+ return;
+ }
+
+ if (enableSharpCifsForSource)
{
_sharpCifsFileSystem.CopyFile(source, target, overwrite);
return;
@@ -1033,7 +1119,7 @@ namespace Emby.Server.Implementations.IO
// On linux and osx the search pattern is case sensitive
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
- if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1)
+ if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1)
{
return Directory.EnumerateFiles(path, "*" + extensions[0], searchOption);
}
diff --git a/Emby.Server.Implementations/IO/MemoryStreamProvider.cs b/Emby.Server.Implementations/IO/MemoryStreamProvider.cs
deleted file mode 100644
index e9ecb7e44..000000000
--- a/Emby.Server.Implementations/IO/MemoryStreamProvider.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.IO;
-using MediaBrowser.Model.IO;
-
-namespace Emby.Server.Implementations.IO
-{
- public class MemoryStreamProvider : IMemoryStreamFactory
- {
- public MemoryStream CreateNew()
- {
- return new MemoryStream();
- }
-
- public MemoryStream CreateNew(int capacity)
- {
- return new MemoryStream(capacity);
- }
-
- public MemoryStream CreateNew(byte[] buffer)
- {
- return new MemoryStream(buffer);
- }
-
- public bool TryGetBuffer(MemoryStream stream, out byte[] buffer)
- {
- buffer = stream.GetBuffer();
- return true;
- }
- }
-}
diff --git a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs
index 0e1f6bb00..a48543bc7 100644
--- a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs
+++ b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs
@@ -136,9 +136,6 @@ namespace Emby.Server.Implementations.IO
if (result.Exists)
{
result.IsDirectory = info.IsDirectory();
- result.IsHidden = info.IsHidden();
-
- result.IsReadOnly = !info.CanWrite();
if (info.IsFile())
{
diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs
new file mode 100644
index 000000000..48a5063e8
--- /dev/null
+++ b/Emby.Server.Implementations/IO/StreamHelper.cs
@@ -0,0 +1,190 @@
+using System.IO;
+using System.Threading;
+using System;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Server.Implementations.IO
+{
+ public class StreamHelper : IStreamHelper
+ {
+ public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
+ {
+ byte[] buffer = new byte[bufferSize];
+ int read;
+ while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
+
+ if (onStarted != null)
+ {
+ onStarted();
+ onStarted = null;
+ }
+ }
+ }
+
+ public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken)
+ {
+ byte[] buffer = new byte[bufferSize];
+
+ if (emptyReadLimit <= 0)
+ {
+ int read;
+ while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
+ }
+
+ return;
+ }
+
+ var eofCount = 0;
+
+ while (eofCount < emptyReadLimit)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var bytesRead = source.Read(buffer, 0, buffer.Length);
+
+ if (bytesRead == 0)
+ {
+ eofCount++;
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ eofCount = 0;
+
+ await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
+ }
+ }
+ }
+
+ const int StreamCopyToBufferSize = 81920;
+ public async Task<int> CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken)
+ {
+ var array = new byte[StreamCopyToBufferSize];
+ int bytesRead;
+ int totalBytesRead = 0;
+
+ while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ var bytesToWrite = bytesRead;
+
+ if (bytesToWrite > 0)
+ {
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+
+ totalBytesRead += bytesRead;
+ }
+ }
+
+ return totalBytesRead;
+ }
+
+ public async Task<int> CopyToAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken)
+ {
+ var array = new byte[StreamCopyToBufferSize];
+ int bytesRead;
+ int totalBytesRead = 0;
+
+ while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
+ {
+ var bytesToWrite = bytesRead;
+
+ if (bytesToWrite > 0)
+ {
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+
+ totalBytesRead += bytesRead;
+ }
+ }
+
+ return totalBytesRead;
+ }
+
+ public async Task CopyToAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
+ {
+ var array = new byte[StreamCopyToBufferSize];
+ int bytesRead;
+
+ while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
+ {
+ var bytesToWrite = Math.Min(bytesRead, copyLength);
+
+ if (bytesToWrite > 0)
+ {
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+ }
+
+ copyLength -= bytesToWrite;
+
+ if (copyLength <= 0)
+ {
+ break;
+ }
+ }
+ }
+
+ public async Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
+ {
+ var array = new byte[StreamCopyToBufferSize];
+ int bytesRead;
+
+ while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ var bytesToWrite = Math.Min(bytesRead, copyLength);
+
+ if (bytesToWrite > 0)
+ {
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+ }
+
+ copyLength -= bytesToWrite;
+
+ if (copyLength <= 0)
+ {
+ break;
+ }
+ }
+ }
+
+ public async Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken)
+ {
+ byte[] buffer = new byte[bufferSize];
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false);
+
+ //var position = fs.Position;
+ //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+
+ if (bytesRead == 0)
+ {
+ await Task.Delay(100).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken)
+ {
+ int bytesRead;
+ int totalBytesRead = 0;
+
+ while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
+
+ totalBytesRead += bytesRead;
+ }
+
+ return totalBytesRead;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 5cd7e4262..be17893d8 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -22,7 +22,7 @@ using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Images
{
public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder
- where T : IHasMetadata
+ where T : BaseItem
{
protected IFileSystem FileSystem { get; private set; }
protected IProviderManager ProviderManager { get; private set; }
@@ -37,12 +37,12 @@ namespace Emby.Server.Implementations.Images
ImageProcessor = imageProcessor;
}
- protected virtual bool Supports(IHasMetadata item)
+ protected virtual bool Supports(BaseItem item)
{
return true;
}
- public virtual ImageType[] GetSupportedImages(IHasMetadata item)
+ public virtual ImageType[] GetSupportedImages(BaseItem item)
{
return new ImageType[]
{
@@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.Images
return updateType;
}
- protected async Task<ItemUpdateType> FetchAsync(IHasMetadata item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ protected Task<ItemUpdateType> FetchAsync(BaseItem item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var image = item.GetImageInfo(imageType, 0);
@@ -83,21 +83,21 @@ namespace Emby.Server.Implementations.Images
{
if (!image.IsLocalFile)
{
- return ItemUpdateType.None;
+ return Task.FromResult(ItemUpdateType.None);
}
if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
{
- return ItemUpdateType.None;
+ return Task.FromResult(ItemUpdateType.None);
}
}
var items = GetItemsWithImages(item);
- return await FetchToFileInternal(item, items, imageType, cancellationToken).ConfigureAwait(false);
+ return FetchToFileInternal(item, items, imageType, cancellationToken);
}
- protected async Task<ItemUpdateType> FetchToFileInternal(IHasMetadata item,
+ protected async Task<ItemUpdateType> FetchToFileInternal(BaseItem item,
List<BaseItem> itemsWithImages,
ImageType imageType,
CancellationToken cancellationToken)
@@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.Images
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPathWithoutExtension));
string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0);
- if (string.IsNullOrWhiteSpace(outputPath))
+ if (string.IsNullOrEmpty(outputPath))
{
return ItemUpdateType.None;
}
@@ -123,14 +123,14 @@ namespace Emby.Server.Implementations.Images
return ItemUpdateType.ImageUpdate;
}
- protected abstract List<BaseItem> GetItemsWithImages(IHasMetadata item);
+ protected abstract List<BaseItem> GetItemsWithImages(BaseItem item);
- protected string CreateThumbCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreateThumbCollage(BaseItem primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 640, 360);
}
- protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasMetadata primaryItem, IEnumerable<BaseItem> items)
+ protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items)
{
return items
.Select(i =>
@@ -149,25 +149,25 @@ namespace Emby.Server.Implementations.Images
}
return null;
})
- .Where(i => !string.IsNullOrWhiteSpace(i));
+ .Where(i => !string.IsNullOrEmpty(i));
}
- protected string CreatePosterCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreatePosterCollage(BaseItem primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 400, 600);
}
- protected string CreateSquareCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath)
+ protected string CreateSquareCollage(BaseItem primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 600, 600);
}
- protected string CreateThumbCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath, int width, int height)
+ protected string CreateThumbCollage(BaseItem primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
return CreateCollage(primaryItem, items, outputPath, width, height);
}
- private string CreateCollage(IHasMetadata primaryItem, List<BaseItem> items, string outputPath, int width, int height)
+ private string CreateCollage(BaseItem primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath));
@@ -198,7 +198,7 @@ namespace Emby.Server.Implementations.Images
get { return "Dynamic Image Provider"; }
}
- protected virtual string CreateImage(IHasMetadata item,
+ protected virtual string CreateImage(BaseItem item,
List<BaseItem> itemsWithImages,
string outputPathWithoutExtension,
ImageType imageType,
@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.Images
get { return 7; }
}
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryServicee)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryServicee)
{
if (!Supports(item))
{
@@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
return false;
}
- protected bool HasChanged(IHasMetadata item, ImageType type)
+ protected bool HasChanged(BaseItem item, ImageType type)
{
var image = item.GetImageInfo(type, 0);
@@ -283,7 +283,7 @@ namespace Emby.Server.Implementations.Images
return true;
}
- protected virtual bool HasChangedByDate(IHasMetadata item, ItemImageInfo image)
+ protected virtual bool HasChangedByDate(BaseItem item, ItemImageInfo image)
{
var age = DateTime.UtcNow - image.DateModified;
if (age.TotalDays <= MaxImageAgeDays)
@@ -293,19 +293,6 @@ namespace Emby.Server.Implementations.Images
return true;
}
- protected List<BaseItem> GetFinalItems(IEnumerable<BaseItem> items)
- {
- return GetFinalItems(items, 4);
- }
-
- protected virtual List<BaseItem> GetFinalItems(IEnumerable<BaseItem> items, int limit)
- {
- return items
- .OrderBy(i => Guid.NewGuid())
- .Take(limit)
- .ToList();
- }
-
public int Order
{
get
@@ -322,7 +309,7 @@ namespace Emby.Server.Implementations.Images
.Select(i => i.GetImagePath(imageType))
.FirstOrDefault();
- if (string.IsNullOrWhiteSpace(image))
+ if (string.IsNullOrEmpty(image))
{
return null;
}
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index 5c3e1dab1..59f0a9fc9 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -22,10 +22,12 @@ namespace Emby.Server.Implementations.Library
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private bool _ignoreDotPrefix;
+
/// <summary>
/// Any folder named in this list will be ignored - can be added to at runtime for extensibility
/// </summary>
- public static readonly List<string> IgnoreFolders = new List<string>
+ public static readonly Dictionary<string, string> IgnoreFolders = new List<string>
{
"metadata",
"ps3_update",
@@ -41,15 +43,24 @@ namespace Emby.Server.Implementations.Library
"#recycle",
// Qnap
- "@Recycle"
+ "@Recycle",
+ ".@__thumb",
+ "$RECYCLE.BIN",
+ "System Volume Information",
+ ".grab",
+
+ // macos
+ ".AppleDouble"
+
+ }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
- };
-
public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager, ILogger logger)
{
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_logger = logger;
+
+ _ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
}
/// <summary>
@@ -67,46 +78,48 @@ namespace Emby.Server.Implementations.Library
}
var filename = fileInfo.Name;
- var isHidden = fileInfo.IsHidden;
var path = fileInfo.FullName;
// Handle mac .DS_Store
// https://github.com/MediaBrowser/MediaBrowser/issues/427
- if (filename.IndexOf("._", StringComparison.OrdinalIgnoreCase) == 0)
+ if (_ignoreDotPrefix)
{
- return true;
- }
-
- // Ignore hidden files and folders
- if (isHidden)
- {
- if (parent == null)
+ if (filename.IndexOf('.') == 0)
{
- var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path));
-
- if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- // Sometimes these are marked hidden
- if (_fileSystem.IsRootPath(path))
- {
- return false;
+ return true;
}
-
- return true;
}
+ // Ignore hidden files and folders
+ //if (fileInfo.IsHidden)
+ //{
+ // if (parent == null)
+ // {
+ // var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path));
+
+ // if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
+ // {
+ // return false;
+ // }
+ // if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+ // {
+ // return false;
+ // }
+ // }
+
+ // // Sometimes these are marked hidden
+ // if (_fileSystem.IsRootPath(path))
+ // {
+ // return false;
+ // }
+
+ // return true;
+ //}
+
if (fileInfo.IsDirectory)
{
// Ignore any folders in our list
- if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
+ if (IgnoreFolders.ContainsKey(filename))
{
return true;
}
@@ -141,6 +154,17 @@ namespace Emby.Server.Implementations.Library
return true;
}
}
+
+ // Ignore samples
+ var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
+
+ if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return true;
+ }
}
return false;
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
new file mode 100644
index 000000000..7c79a7c69
--- /dev/null
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Cryptography;
+
+namespace Emby.Server.Implementations.Library
+{
+ public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
+ {
+ private readonly ICryptoProvider _cryptographyProvider;
+ public DefaultAuthenticationProvider(ICryptoProvider crypto)
+ {
+ _cryptographyProvider = crypto;
+ }
+
+ public string Name => "Default";
+
+ public bool IsEnabled => true;
+
+ public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
+ {
+ if (resolvedUser == null)
+ {
+ throw new Exception("Invalid username or password");
+ }
+
+ var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase);
+
+ if (!success)
+ {
+ throw new Exception("Invalid username or password");
+ }
+
+ return Task.FromResult(new ProviderAuthenticationResult
+ {
+ Username = username
+ });
+ }
+
+ public Task<bool> HasPassword(User user)
+ {
+ var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user));
+ return Task.FromResult(hasConfiguredPassword);
+ }
+
+ private bool IsPasswordEmpty(User user, string passwordHash)
+ {
+ return string.Equals(passwordHash, GetEmptyHashedString(user), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public Task ChangePassword(User user, string newPassword)
+ {
+ string newPasswordHash = null;
+
+ if (newPassword != null)
+ {
+ newPasswordHash = GetHashedString(user, newPassword);
+ }
+
+ if (string.IsNullOrWhiteSpace(newPasswordHash))
+ {
+ throw new ArgumentNullException("newPasswordHash");
+ }
+
+ user.Password = newPasswordHash;
+
+ return Task.CompletedTask;
+ }
+
+ public string GetPasswordHash(User user)
+ {
+ return string.IsNullOrEmpty(user.Password)
+ ? GetEmptyHashedString(user)
+ : user.Password;
+ }
+
+ public string GetEmptyHashedString(User user)
+ {
+ return GetHashedString(user, string.Empty);
+ }
+
+ /// <summary>
+ /// Gets the hashed string.
+ /// </summary>
+ public string GetHashedString(User user, string str)
+ {
+ var salt = user.Salt;
+ if (salt != null)
+ {
+ // return BCrypt.HashPassword(str, salt);
+ }
+
+ // legacy
+ return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
new file mode 100644
index 000000000..186ec63da
--- /dev/null
+++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Controller.Library;
+
+namespace Emby.Server.Implementations.Library
+{
+ public class ExclusiveLiveStream : ILiveStream
+ {
+ public int ConsumerCount { get; set; }
+ public string OriginalStreamId { get; set; }
+
+ public string TunerHostId => null;
+
+ public bool EnableStreamSharing { get; set; }
+ public MediaSourceInfo MediaSource { get; set; }
+
+ public string UniqueId { get; private set; }
+
+ private Func<Task> _closeFn;
+
+ public ExclusiveLiveStream(MediaSourceInfo mediaSource, Func<Task> closeFn)
+ {
+ MediaSource = mediaSource;
+ EnableStreamSharing = false;
+ _closeFn = closeFn;
+ ConsumerCount = 1;
+ UniqueId = Guid.NewGuid().ToString("N");
+ }
+
+ public Task Close()
+ {
+ return _closeFn();
+ }
+
+ public Task Open(CancellationToken openCancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 2934a5147..31af9370c 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -44,6 +44,9 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Tasks;
+using Emby.Server.Implementations.Playlists;
+using MediaBrowser.Providers.MediaInfo;
+using MediaBrowser.Controller;
namespace Emby.Server.Implementations.Library
{
@@ -71,12 +74,6 @@ namespace Emby.Server.Implementations.Library
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
/// <summary>
- /// Gets the list of BasePluginFolders added by plugins
- /// </summary>
- /// <value>The plugin folders.</value>
- private IVirtualFolderCreator[] PluginFolderCreators { get; set; }
-
- /// <summary>
/// Gets the list of currently registered entity resolvers
/// </summary>
/// <value>The entity resolvers enumerable.</value>
@@ -140,6 +137,7 @@ namespace Emby.Server.Implementations.Library
private readonly Func<IProviderManager> _providerManagerFactory;
private readonly Func<IUserViewManager> _userviewManager;
public bool IsScanRunning { get; private set; }
+ private IServerApplicationHost _appHost;
/// <summary>
/// The _library items cache
@@ -167,7 +165,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
- public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager)
+ public LibraryManager(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager)
{
_logger = logger;
_taskManager = taskManager;
@@ -178,6 +176,7 @@ namespace Emby.Server.Implementations.Library
_fileSystem = fileSystem;
_providerManagerFactory = providerManagerFactory;
_userviewManager = userviewManager;
+ _appHost = appHost;
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -195,14 +194,12 @@ namespace Emby.Server.Implementations.Library
/// <param name="itemComparers">The item comparers.</param>
/// <param name="postscanTasks">The postscan tasks.</param>
public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
- IEnumerable<IVirtualFolderCreator> pluginFolders,
IEnumerable<IItemResolver> resolvers,
IEnumerable<IIntroProvider> introProviders,
IEnumerable<IBaseItemComparer> itemComparers,
IEnumerable<ILibraryPostScanTask> postscanTasks)
{
EntityResolutionIgnoreRules = rules.ToArray();
- PluginFolderCreators = pluginFolders.ToArray();
EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
IntroProviders = introProviders.ToArray();
@@ -302,7 +299,7 @@ namespace Emby.Server.Implementations.Library
}
else
{
- if (!(item is Video))
+ if (!(item is Video) && !(item is LiveTvChannel))
{
return;
}
@@ -311,13 +308,47 @@ namespace Emby.Server.Implementations.Library
LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
}
- public async Task DeleteItem(BaseItem item, DeleteOptions options)
+ public void DeleteItem(BaseItem item, DeleteOptions options)
+ {
+ DeleteItem(item, options, false);
+ }
+
+ public void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException("item");
+ }
+
+ var parent = item.GetOwner() ?? item.GetParent();
+
+ DeleteItem(item, options, parent, notifyParentItem);
+ }
+
+ public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
+ if (item.SourceType == SourceType.Channel)
+ {
+ if (options.DeleteFromExternalProvider)
+ {
+ try
+ {
+ var task = BaseItem.ChannelManager.DeleteItem(item);
+ Task.WaitAll(task);
+ }
+ catch (ArgumentException)
+ {
+ // channel no longer installed
+ }
+ }
+ options.DeleteFileLocation = false;
+ }
+
if (item is LiveTvProgram)
{
_logger.Debug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
@@ -335,10 +366,6 @@ namespace Emby.Server.Implementations.Library
item.Id);
}
- var parent = item.IsOwnedItem ? item.GetOwner() : item.GetParent();
-
- var locationType = item.LocationType;
-
var children = item.IsFolder
? ((Folder)item).GetRecursiveChildren(false).ToList()
: new List<BaseItem>();
@@ -361,7 +388,7 @@ namespace Emby.Server.Implementations.Library
}
}
- if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual)
+ if (options.DeleteFileLocation && item.IsFileProtocol)
{
// Assume only the first is required
// Add this flag to GetDeletePaths if required in the future
@@ -407,33 +434,10 @@ namespace Emby.Server.Implementations.Library
isRequiredForDelete = false;
}
-
- if (parent != null)
- {
- var parentFolder = parent as Folder;
- if (parentFolder != null)
- {
- await parentFolder.ValidateChildren(new SimpleProgress<double>(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false).ConfigureAwait(false);
- }
- else
- {
- await parent.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), CancellationToken.None).ConfigureAwait(false);
- }
- }
- }
- else if (parent != null)
- {
- var parentFolder = parent as Folder;
- if (parentFolder != null)
- {
- parentFolder.RemoveChild(item);
- }
- else
- {
- await parent.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), CancellationToken.None).ConfigureAwait(false);
- }
}
+ item.SetParent(null);
+
ItemRepository.DeleteItem(item.Id, CancellationToken.None);
foreach (var child in children)
{
@@ -497,7 +501,7 @@ namespace Emby.Server.Implementations.Library
private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive)
{
- if (string.IsNullOrWhiteSpace(key))
+ if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
@@ -544,7 +548,7 @@ namespace Emby.Server.Implementations.Library
var fullPath = fileInfo.FullName;
- if (string.IsNullOrWhiteSpace(collectionType) && parent != null)
+ if (string.IsNullOrEmpty(collectionType) && parent != null)
{
collectionType = GetContentTypeOverride(fullPath, true);
}
@@ -572,7 +576,26 @@ namespace Emby.Server.Implementations.Library
// When resolving the root, we need it's grandchildren (children of user views)
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
- var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+ FileSystemMetadata[] files;
+ var isVf = args.IsVf;
+
+ try
+ {
+ files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _appHost, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || isVf);
+ }
+ catch (Exception ex)
+ {
+ if (parent != null && parent.IsPhysicalRoot)
+ {
+ _logger.ErrorException("Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", ex, isPhysicalRoot, isVf);
+
+ files = new FileSystemMetadata[] { };
+ }
+ else
+ {
+ throw;
+ }
+ }
// Need to remove subpaths that may have been resolved from shortcuts
// Example: if \\server\movies exists, then strip out \\server\movies\action
@@ -717,42 +740,43 @@ namespace Emby.Server.Implementations.Library
}
// Add in the plug-in folders
- foreach (var child in PluginFolderCreators)
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "playlists");
+
+ _fileSystem.CreateDirectory(path);
+
+ Folder folder = new PlaylistsFolder
{
- var folder = child.GetFolder();
+ Path = path
+ };
- if (folder != null)
+ if (folder.Id.Equals(Guid.Empty))
+ {
+ if (string.IsNullOrEmpty(folder.Path))
{
- if (folder.Id == Guid.Empty)
- {
- if (string.IsNullOrWhiteSpace(folder.Path))
- {
- folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
- }
- else
- {
- folder.Id = GetNewItemId(folder.Path, folder.GetType());
- }
- }
+ folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
+ }
+ else
+ {
+ folder.Id = GetNewItemId(folder.Path, folder.GetType());
+ }
+ }
- var dbItem = GetItemById(folder.Id) as BasePluginFolder;
+ var dbItem = GetItemById(folder.Id) as BasePluginFolder;
- if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase))
- {
- folder = dbItem;
- }
+ if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase))
+ {
+ folder = dbItem;
+ }
- if (folder.ParentId != rootFolder.Id)
- {
- folder.ParentId = rootFolder.Id;
- folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
- }
+ if (folder.ParentId != rootFolder.Id)
+ {
+ folder.ParentId = rootFolder.Id;
+ folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+ }
- rootFolder.AddVirtualChild(folder);
+ rootFolder.AddVirtualChild(folder);
- RegisterItem(folder);
- }
- }
+ RegisterItem(folder);
return rootFolder;
}
@@ -798,16 +822,18 @@ namespace Emby.Server.Implementations.Library
// If this returns multiple items it could be tricky figuring out which one is correct.
// In most cases, the newest one will be and the others obsolete but not yet cleaned up
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
+ //_logger.Info("FindByPath {0}", path);
+
var query = new InternalItemsQuery
{
Path = path,
IsFolder = isFolder,
- OrderBy = new[] { ItemSortBy.DateCreated }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.DateCreated }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
Limit = 1,
DtoOptions = new DtoOptions(true)
};
@@ -957,7 +983,7 @@ namespace Emby.Server.Implementations.Library
Path = path
};
- CreateItem(item, CancellationToken.None);
+ CreateItem(item, null);
}
return item;
@@ -997,7 +1023,7 @@ namespace Emby.Server.Implementations.Library
// Just run the scheduled task so that the user can see it
_taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
/// <summary>
@@ -1031,48 +1057,45 @@ namespace Emby.Server.Implementations.Library
}
}
- private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)
+ private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken)
{
- _logger.Info("Validating media library");
-
- // Ensure these objects are lazy loaded.
- // Without this there is a deadlock that will need to be investigated
var rootChildren = RootFolder.Children.ToList();
rootChildren = GetUserRootFolder().Children.ToList();
await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- progress.Report(.5);
-
// Start by just validating the children of the root, but go no further
await RootFolder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false);
- progress.Report(1);
-
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
await GetUserRootFolder().ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
- progress.Report(2);
// Quickly scan CollectionFolders for changes
foreach (var folder in GetUserRootFolder().Children.OfType<Folder>().ToList())
{
await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
- progress.Report(3);
+ }
+
+ private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ _logger.Info("Validating media library");
+
+ await ValidateTopLibraryFolders(cancellationToken).ConfigureAwait(false);
var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct => progress.Report(3 + pct * .72));
+ innerProgress.RegisterAction(pct => progress.Report(pct * .96));
// Now validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false);
- progress.Report(75);
+ progress.Report(96);
innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct => progress.Report(75 + pct * .25));
+ innerProgress.RegisterAction(pct => progress.Report(96 + (pct * .04)));
// Run post-scan tasks
await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false);
@@ -1102,8 +1125,13 @@ namespace Emby.Server.Implementations.Library
innerProgress.RegisterAction(pct =>
{
- double innerPercent = currentNumComplete * 100 + pct;
+ double innerPercent = pct;
+ innerPercent /= 100;
+ innerPercent += currentNumComplete;
+
innerPercent /= numTasks;
+ innerPercent *= 100;
+
progress.Report(innerPercent);
});
@@ -1163,7 +1191,19 @@ namespace Emby.Server.Implementations.Library
Locations = _fileSystem.GetFilePaths(dir, false)
.Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
- .Select(_fileSystem.ResolveShortcut)
+ .Select(i =>
+ {
+ try
+ {
+ return _appHost.ExpandVirtualPath(_fileSystem.ResolveShortcut(i));
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error resolving shortcut file {0}", ex, i);
+ return null;
+ }
+ })
+ .Where(i => i != null)
.OrderBy(i => i)
.ToArray(),
@@ -1197,7 +1237,7 @@ namespace Emby.Server.Implementations.Library
{
return _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false)
.Select(i => _fileSystem.GetFileNameWithoutExtension(i))
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ .FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
/// <summary>
@@ -1208,7 +1248,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="System.ArgumentNullException">id</exception>
public BaseItem GetItemById(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -1234,9 +1274,9 @@ namespace Emby.Server.Implementations.Library
public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
{
- if (query.Recursive && query.ParentId.HasValue)
+ if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
{
- var parent = GetItemById(query.ParentId.Value);
+ var parent = GetItemById(query.ParentId);
if (parent != null)
{
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
@@ -1258,9 +1298,9 @@ namespace Emby.Server.Implementations.Library
public int GetCount(InternalItemsQuery query)
{
- if (query.Recursive && query.ParentId.HasValue)
+ if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
{
- var parent = GetItemById(query.ParentId.Value);
+ var parent = GetItemById(query.ParentId);
if (parent != null)
{
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
@@ -1391,7 +1431,7 @@ namespace Emby.Server.Implementations.Library
return;
}
- var parents = query.AncestorIds.Select(i => GetItemById(new Guid(i))).ToList();
+ var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList();
if (parents.All(i =>
{
@@ -1406,13 +1446,13 @@ namespace Emby.Server.Implementations.Library
}))
{
// Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
- query.AncestorIds = new string[] { };
+ query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
+ query.AncestorIds = Array.Empty<Guid>();
// Prevent searching in all libraries due to empty filter
if (query.TopParentIds.Length == 0)
{
- query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
+ query.TopParentIds = new[] { Guid.NewGuid() };
}
}
}
@@ -1430,9 +1470,9 @@ namespace Emby.Server.Implementations.Library
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
{
- if (query.Recursive && query.ParentId.HasValue)
+ if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
{
- var parent = GetItemById(query.ParentId.Value);
+ var parent = GetItemById(query.ParentId);
if (parent != null)
{
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
@@ -1472,23 +1512,23 @@ namespace Emby.Server.Implementations.Library
}))
{
// Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
+ query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
// Prevent searching in all libraries due to empty filter
if (query.TopParentIds.Length == 0)
{
- query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
+ query.TopParentIds = new[] { Guid.NewGuid() };
}
}
else
{
// We need to be able to query from any arbitrary ancestor up the tree
- query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
+ query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).ToArray();
// Prevent searching in all libraries due to empty filter
if (query.AncestorIds.Length == 0)
{
- query.AncestorIds = new[] { Guid.NewGuid().ToString("N") };
+ query.AncestorIds = new[] { Guid.NewGuid() };
}
}
@@ -1498,22 +1538,21 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
- !query.ParentId.HasValue &&
+ query.ParentId.Equals(Guid.Empty) &&
query.ChannelIds.Length == 0 &&
query.TopParentIds.Length == 0 &&
- string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) &&
- string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey) &&
+ string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) &&
+ string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
query.ItemIds.Length == 0)
{
var userViews = _userviewManager().GetUserViews(new UserViewQuery
{
- UserId = user.Id.ToString("N"),
+ UserId = user.Id,
IncludeHidden = true,
IncludeExternalContent = allowExternalContent
+ });
- }, CancellationToken.None).Result;
-
- query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).Select(i => i.ToString("N")).ToArray();
+ query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).ToArray();
}
}
@@ -1527,48 +1566,38 @@ namespace Emby.Server.Implementations.Library
{
return new[] { view.Id };
}
- if (string.Equals(view.ViewType, CollectionType.Channels))
- {
- var channelResult = BaseItem.ChannelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = user.Id.ToString("N")
-
- }, CancellationToken.None).Result;
-
- return channelResult.Items.Select(i => i.Id);
- }
// Translate view into folders
- if (view.DisplayParentId != Guid.Empty)
+ if (!view.DisplayParentId.Equals(Guid.Empty))
{
var displayParent = GetItemById(view.DisplayParentId);
if (displayParent != null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
- return new Guid[] { };
+ return Array.Empty<Guid>();
}
- if (view.ParentId != Guid.Empty)
+ if (!view.ParentId.Equals(Guid.Empty))
{
var displayParent = GetItemById(view.ParentId);
if (displayParent != null)
{
return GetTopParentIdsForQuery(displayParent, user);
}
- return new Guid[] { };
+ return Array.Empty<Guid>();
}
// Handle grouping
- if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
+ if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
{
- return user.RootFolder
+ return GetUserRootFolder()
.GetChildren(user, true)
.OfType<CollectionFolder>()
- .Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase))
+ .Where(i => string.IsNullOrEmpty(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase))
.Where(i => user.IsFolderGrouped(i.Id))
.SelectMany(i => GetTopParentIdsForQuery(i, user));
}
- return new Guid[] { };
+ return Array.Empty<Guid>();
}
var collectionFolder = item as CollectionFolder;
@@ -1582,7 +1611,7 @@ namespace Emby.Server.Implementations.Library
{
return new[] { topParent.Id };
}
- return new Guid[] { };
+ return Array.Empty<Guid>();
}
/// <summary>
@@ -1737,7 +1766,7 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<Tuple<string, SortOrder>> orderByList)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderByList)
{
var isFirst = true;
@@ -1802,9 +1831,9 @@ namespace Emby.Server.Implementations.Library
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- public void CreateItem(BaseItem item, CancellationToken cancellationToken)
+ public void CreateItem(BaseItem item, BaseItem parent)
{
- CreateItems(new[] { item }, item.GetParent(), cancellationToken);
+ CreateItems(new[] { item }, parent, CancellationToken.None);
}
/// <summary>
@@ -1828,6 +1857,12 @@ namespace Emby.Server.Implementations.Library
{
foreach (var item in list)
{
+ // With the live tv guide this just creates too much noise
+ if (item.SourceType != SourceType.Library)
+ {
+ continue;
+ }
+
try
{
ItemAdded(this, new ItemChangeEventArgs
@@ -1854,46 +1889,65 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Updates the item.
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="updateReason">The update reason.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ public void UpdateItems(List<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- var locationType = item.LocationType;
- if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
+ foreach (var item in items)
{
- _providerManagerFactory().SaveMetadata(item, updateReason);
- }
+ if (item.IsFileProtocol)
+ {
+ _providerManagerFactory().SaveMetadata(item, updateReason);
+ }
- item.DateLastSaved = DateTime.UtcNow;
+ item.DateLastSaved = DateTime.UtcNow;
- var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
- _logger.Debug("Saving {0} to database.", logName);
+ RegisterItem(item);
+ }
- ItemRepository.SaveItem(item, cancellationToken);
+ //var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
+ //_logger.Debug("Saving {0} to database.", logName);
- RegisterItem(item);
+ ItemRepository.SaveItems(items, cancellationToken);
if (ItemUpdated != null)
{
- try
+ foreach (var item in items)
{
- ItemUpdated(this, new ItemChangeEventArgs
+ // With the live tv guide this just creates too much noise
+ if (item.SourceType != SourceType.Library)
{
- Item = item,
- Parent = item.GetParent(),
- UpdateReason = updateReason
- });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in ItemUpdated event handler", ex);
+ continue;
+ }
+
+ try
+ {
+ ItemUpdated(this, new ItemChangeEventArgs
+ {
+ Item = item,
+ Parent = parent,
+ UpdateReason = updateReason
+ });
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in ItemUpdated event handler", ex);
+ }
}
}
}
/// <summary>
+ /// Updates the item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="updateReason">The update reason.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ {
+ UpdateItems(new List<BaseItem> { item }, parent, updateReason, cancellationToken);
+ }
+
+ /// <summary>
/// Reports the item removed.
/// </summary>
/// <param name="item">The item.</param>
@@ -1995,12 +2049,12 @@ namespace Emby.Server.Implementations.Library
public string GetContentType(BaseItem item)
{
string configuredContentType = GetConfiguredContentType(item, false);
- if (!string.IsNullOrWhiteSpace(configuredContentType))
+ if (!string.IsNullOrEmpty(configuredContentType))
{
return configuredContentType;
}
configuredContentType = GetConfiguredContentType(item, true);
- if (!string.IsNullOrWhiteSpace(configuredContentType))
+ if (!string.IsNullOrEmpty(configuredContentType))
{
return configuredContentType;
}
@@ -2011,14 +2065,14 @@ namespace Emby.Server.Implementations.Library
{
var type = GetTopFolderContentType(item);
- if (!string.IsNullOrWhiteSpace(type))
+ if (!string.IsNullOrEmpty(type))
{
return type;
}
return item.GetParents()
.Select(GetConfiguredContentType)
- .LastOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ .LastOrDefault(i => !string.IsNullOrEmpty(i));
}
public string GetConfiguredContentType(BaseItem item)
@@ -2043,7 +2097,7 @@ namespace Emby.Server.Implementations.Library
private string GetContentTypeOverride(string path, bool inherit)
{
- var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
+ var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrEmpty(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
if (nameValuePair != null)
{
return nameValuePair.Value;
@@ -2058,16 +2112,21 @@ namespace Emby.Server.Implementations.Library
return null;
}
- while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null)
+ while (!item.ParentId.Equals(Guid.Empty))
{
- item = item.GetParent();
+ var parent = item.GetParent();
+ if (parent == null || parent is AggregateFolder)
+ {
+ break;
+ }
+ item = parent;
}
return GetUserRootFolder().Children
.OfType<ICollectionFolder>()
.Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path))
.Select(i => i.CollectionType)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ .FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
@@ -2076,18 +2135,16 @@ namespace Emby.Server.Implementations.Library
public UserView GetNamedView(User user,
string name,
string viewType,
- string sortName,
- CancellationToken cancellationToken)
+ string sortName)
{
- return GetNamedView(user, name, null, viewType, sortName, cancellationToken);
+ return GetNamedView(user, name, Guid.Empty, viewType, sortName);
}
public UserView GetNamedView(string name,
string viewType,
- string sortName,
- CancellationToken cancellationToken)
+ string sortName)
{
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views");
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views");
path = Path.Combine(path, _fileSystem.GetValidFilename(viewType));
@@ -2111,32 +2168,15 @@ namespace Emby.Server.Implementations.Library
ForcedSortName = sortName
};
- CreateItem(item, cancellationToken);
+ CreateItem(item, null);
refresh = true;
}
- if (!refresh)
- {
- refresh = DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- }
-
- if (!refresh && item.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
- }
-
if (refresh)
{
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- // Not sure why this is necessary but need to figure it out
- // View images are not getting utilized without this
- ForceSave = true
-
- }, RefreshPriority.Normal);
+ _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Normal);
}
return item;
@@ -2144,12 +2184,12 @@ namespace Emby.Server.Implementations.Library
public UserView GetNamedView(User user,
string name,
- string parentId,
+ Guid parentId,
string viewType,
- string sortName,
- CancellationToken cancellationToken)
+ string sortName)
{
- var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty);
+ var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N");
+ var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
var id = GetNewItemId(idValues, typeof(UserView));
@@ -2174,19 +2214,16 @@ namespace Emby.Server.Implementations.Library
UserId = user.Id
};
- if (!string.IsNullOrWhiteSpace(parentId))
- {
- item.DisplayParentId = new Guid(parentId);
- }
+ item.DisplayParentId = parentId;
- CreateItem(item, cancellationToken);
+ CreateItem(item, null);
isNew = true;
}
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && item.DisplayParentId != Guid.Empty)
+ if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2207,8 +2244,7 @@ namespace Emby.Server.Implementations.Library
public UserView GetShadowView(BaseItem parent,
string viewType,
- string sortName,
- CancellationToken cancellationToken)
+ string sortName)
{
if (parent == null)
{
@@ -2244,14 +2280,14 @@ namespace Emby.Server.Implementations.Library
item.DisplayParentId = parentId;
- CreateItem(item, cancellationToken);
+ CreateItem(item, null);
isNew = true;
}
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && item.DisplayParentId != Guid.Empty)
+ if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2271,19 +2307,19 @@ namespace Emby.Server.Implementations.Library
}
public UserView GetNamedView(string name,
- string parentId,
+ Guid parentId,
string viewType,
string sortName,
- string uniqueId,
- CancellationToken cancellationToken)
+ string uniqueId)
{
- if (string.IsNullOrWhiteSpace(name))
+ if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
- var idValues = "37_namedview_" + name + (parentId ?? string.Empty) + (viewType ?? string.Empty);
- if (!string.IsNullOrWhiteSpace(uniqueId))
+ var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N");
+ var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
+ if (!string.IsNullOrEmpty(uniqueId))
{
idValues += uniqueId;
}
@@ -2310,12 +2346,9 @@ namespace Emby.Server.Implementations.Library
ForcedSortName = sortName
};
- if (!string.IsNullOrWhiteSpace(parentId))
- {
- item.DisplayParentId = new Guid(parentId);
- }
+ item.DisplayParentId = parentId;
- CreateItem(item, cancellationToken);
+ CreateItem(item, null);
isNew = true;
}
@@ -2323,12 +2356,12 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
{
item.ViewType = viewType;
- item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+ item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
}
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && item.DisplayParentId != Guid.Empty)
+ if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2346,6 +2379,13 @@ namespace Emby.Server.Implementations.Library
return item;
}
+ public void AddExternalSubtitleStreams(List<MediaStream> streams,
+ string videoPath,
+ string[] files)
+ {
+ new SubtitleResolver(BaseItem.LocalizationManager, _fileSystem).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
+ }
+
public bool IsVideoFile(string path, LibraryOptions libraryOptions)
{
var resolver = new VideoResolver(GetNamingOptions());
@@ -2370,19 +2410,25 @@ namespace Emby.Server.Implementations.Library
public int? GetSeasonNumberFromPath(string path)
{
- return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber;
+ return new SeasonPathParser(GetNamingOptions()).Parse(path, true, true).SeasonNumber;
}
- public bool FillMissingEpisodeNumbersFromPath(Episode episode)
+ public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
{
+ var series = episode.Series;
+ bool? isAbsoluteNaming = series == null ? false : string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
+ if (!isAbsoluteNaming.Value)
+ {
+ // In other words, no filter applied
+ isAbsoluteNaming = null;
+ }
+
var resolver = new EpisodeResolver(GetNamingOptions());
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
- var locationType = episode.LocationType;
-
- var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
- resolver.Resolve(episode.Path, isFolder) :
+ var episodeInfo = episode.IsFileProtocol ?
+ resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) :
new Emby.Naming.TV.EpisodeInfo();
if (episodeInfo == null)
@@ -2428,105 +2474,67 @@ namespace Emby.Server.Implementations.Library
changed = true;
}
}
-
- if (!episode.ParentIndexNumber.HasValue)
- {
- var season = episode.Season;
-
- if (season != null)
- {
- episode.ParentIndexNumber = season.IndexNumber;
- }
-
- if (episode.ParentIndexNumber.HasValue)
- {
- changed = true;
- }
- }
}
else
{
- if (!episode.IndexNumber.HasValue)
+ if (!episode.IndexNumber.HasValue || forceRefresh)
{
- episode.IndexNumber = episodeInfo.EpisodeNumber;
-
- if (episode.IndexNumber.HasValue)
+ if (episode.IndexNumber != episodeInfo.EpisodeNumber)
{
changed = true;
}
+ episode.IndexNumber = episodeInfo.EpisodeNumber;
}
- if (!episode.IndexNumberEnd.HasValue)
+ if (!episode.IndexNumberEnd.HasValue || forceRefresh)
{
- episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
-
- if (episode.IndexNumberEnd.HasValue)
+ if (episode.IndexNumberEnd != episodeInfo.EndingEpsiodeNumber)
{
changed = true;
}
+ episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
}
- if (!episode.ParentIndexNumber.HasValue)
+ if (!episode.ParentIndexNumber.HasValue || forceRefresh)
{
- episode.ParentIndexNumber = episodeInfo.SeasonNumber;
-
- if (!episode.ParentIndexNumber.HasValue)
- {
- var season = episode.Season;
-
- if (season != null)
- {
- episode.ParentIndexNumber = season.IndexNumber;
- }
- }
-
- if (episode.ParentIndexNumber.HasValue)
+ if (episode.ParentIndexNumber != episodeInfo.SeasonNumber)
{
changed = true;
}
+ episode.ParentIndexNumber = episodeInfo.SeasonNumber;
}
}
- return changed;
- }
-
- public NamingOptions GetNamingOptions()
- {
- return GetNamingOptions(true);
- }
-
- public NamingOptions GetNamingOptions(bool allowOptimisticEpisodeDetection)
- {
- if (!allowOptimisticEpisodeDetection)
+ if (!episode.ParentIndexNumber.HasValue)
{
- if (_namingOptionsWithoutOptimisticEpisodeDetection == null)
- {
- var namingOptions = new ExtendedNamingOptions();
+ var season = episode.Season;
- InitNamingOptions(namingOptions);
- namingOptions.EpisodeExpressions = namingOptions.EpisodeExpressions
- .Where(i => i.IsNamed && !i.IsOptimistic)
- .ToList();
-
- _namingOptionsWithoutOptimisticEpisodeDetection = namingOptions;
+ if (season != null)
+ {
+ episode.ParentIndexNumber = season.IndexNumber;
}
- return _namingOptionsWithoutOptimisticEpisodeDetection;
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ changed = true;
+ }
}
+ return changed;
+ }
+
+ public NamingOptions GetNamingOptions()
+ {
return GetNamingOptionsInternal();
}
- private NamingOptions _namingOptionsWithoutOptimisticEpisodeDetection;
private NamingOptions _namingOptions;
private string[] _videoFileExtensions;
private NamingOptions GetNamingOptionsInternal()
{
if (_namingOptions == null)
{
- var options = new ExtendedNamingOptions();
-
- InitNamingOptions(options);
+ var options = new NamingOptions();
_namingOptions = options;
_videoFileExtensions = _namingOptions.VideoFileExtensions.ToArray();
@@ -2535,27 +2543,6 @@ namespace Emby.Server.Implementations.Library
return _namingOptions;
}
- private void InitNamingOptions(NamingOptions options)
- {
- // These cause apps to have problems
- options.AudioFileExtensions.Remove(".m3u");
- options.AudioFileExtensions.Remove(".wpl");
-
- //if (!libraryOptions.EnableArchiveMediaFiles)
- {
- options.AudioFileExtensions.Remove(".rar");
- options.AudioFileExtensions.Remove(".zip");
- }
-
- //if (!libraryOptions.EnableArchiveMediaFiles)
- {
- options.VideoFileExtensions.Remove(".rar");
- options.VideoFileExtensions.Remove(".zip");
- }
-
- options.VideoFileExtensions.Add(".tp");
- }
-
public ItemLookupInfo ParseName(string name)
{
var resolver = new VideoResolver(GetNamingOptions());
@@ -2606,12 +2593,11 @@ namespace Emby.Server.Implementations.Library
{
video = dbItem;
}
- else
- {
- // item is new
- video.ExtraType = ExtraType.Trailer;
- }
- video.TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
+
+ video.ParentId = Guid.Empty;
+ video.OwnerId = owner.Id;
+ video.ExtraType = ExtraType.Trailer;
+ video.TrailerTypes = new [] { TrailerType.LocalTrailer };
return video;
@@ -2625,7 +2611,7 @@ namespace Emby.Server.Implementations.Library
{
var namingOptions = GetNamingOptions();
- var files = fileSystemChildren.Where(i => i.IsDirectory)
+ var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => ExtrasSubfolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.ToList();
@@ -2653,6 +2639,9 @@ namespace Emby.Server.Implementations.Library
video = dbItem;
}
+ video.ParentId = Guid.Empty;
+ video.OwnerId = owner.Id;
+
SetExtraTypeFromFilename(video);
return video;
@@ -2756,7 +2745,7 @@ namespace Emby.Server.Implementations.Library
private void SetExtraTypeFromFilename(Video item)
{
- var resolver = new ExtraResolver(GetNamingOptions(), new RegexProvider());
+ var resolver = new ExtraResolver(GetNamingOptions());
var result = resolver.GetExtraInfo(item.Path);
@@ -2841,7 +2830,7 @@ namespace Emby.Server.Implementations.Library
ItemRepository.UpdatePeople(item.Id, people);
}
- public async Task<ItemImageInfo> ConvertImageToLocal(IHasMetadata item, ItemImageInfo image, int imageIndex)
+ public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
{
foreach (var url in image.Path.Split('|'))
{
@@ -2872,7 +2861,7 @@ namespace Emby.Server.Implementations.Library
throw new InvalidOperationException();
}
- public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary)
+ public async Task AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary)
{
if (string.IsNullOrWhiteSpace(name))
{
@@ -2910,7 +2899,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Path.Combine(virtualFolderPath, collectionType + ".collection");
- _fileSystem.WriteAllBytes(path, new byte[] { });
+ _fileSystem.WriteAllBytes(path, Array.Empty<byte>());
}
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
@@ -2925,26 +2914,30 @@ namespace Emby.Server.Implementations.Library
}
finally
{
- Task.Run(() =>
+ if (refreshLibrary)
{
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- var task = Task.Delay(1000);
- // Have to block here to allow exceptions to bubble
- Task.WaitAll(task);
+ await ValidateTopLibraryFolders(CancellationToken.None).ConfigureAwait(false);
- _libraryMonitorFactory().Start();
- }
- });
+ StartScanInBackground();
+ }
+ else
+ {
+ // Need to add a delay here or directory watchers may still pick up the changes
+ await Task.Delay(1000).ConfigureAwait(false);
+ _libraryMonitorFactory().Start();
+ }
}
}
+ private void StartScanInBackground()
+ {
+ Task.Run(() =>
+ {
+ // No need to start if scanning the library because it will handle it
+ ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
+ });
+ }
+
private bool ValidateNetworkPath(string path)
{
//if (Environment.OSVersion.Platform == PlatformID.Win32NT)
@@ -3003,7 +2996,7 @@ namespace Emby.Server.Implementations.Library
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
}
- _fileSystem.CreateShortcut(lnk, path);
+ _fileSystem.CreateShortcut(lnk, _appHost.ReverseVirtualPath(path));
RemoveContentTypeOverrides(path);
@@ -3079,7 +3072,7 @@ namespace Emby.Server.Implementations.Library
}
}
- public void RemoveVirtualFolder(string name, bool refreshLibrary)
+ public async Task RemoveVirtualFolder(string name, bool refreshLibrary)
{
if (string.IsNullOrWhiteSpace(name))
{
@@ -3103,23 +3096,20 @@ namespace Emby.Server.Implementations.Library
}
finally
{
- Task.Run(() =>
+ CollectionFolder.OnCollectionFolderChange();
+
+ if (refreshLibrary)
{
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- var task = Task.Delay(1000);
- // Have to block here to allow exceptions to bubble
- Task.WaitAll(task);
+ await ValidateTopLibraryFolders(CancellationToken.None).ConfigureAwait(false);
- _libraryMonitorFactory().Start();
- }
- });
+ StartScanInBackground();
+ }
+ else
+ {
+ // Need to add a delay here or directory watchers may still pick up the changes
+ await Task.Delay(1000).ConfigureAwait(false);
+ _libraryMonitorFactory().Start();
+ }
}
}
@@ -3157,7 +3147,7 @@ namespace Emby.Server.Implementations.Library
public void RemoveMediaPath(string virtualFolderName, string mediaPath)
{
- if (string.IsNullOrWhiteSpace(mediaPath))
+ if (string.IsNullOrEmpty(mediaPath))
{
throw new ArgumentNullException("mediaPath");
}
@@ -3172,7 +3162,7 @@ namespace Emby.Server.Implementations.Library
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
.Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
- .FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
+ .FirstOrDefault(f => _appHost.ExpandVirtualPath(_fileSystem.ResolveShortcut(f)).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(shortcut))
{
diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
new file mode 100644
index 000000000..e027e133f
--- /dev/null
+++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
+using System.Collections.Generic;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Common.Configuration;
+using System.IO;
+using MediaBrowser.Common.Extensions;
+
+namespace Emby.Server.Implementations.Library
+{
+ public class LiveStreamHelper
+ {
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly ILogger _logger;
+
+ private IJsonSerializer _json;
+ private IApplicationPaths _appPaths;
+
+ public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths)
+ {
+ _mediaEncoder = mediaEncoder;
+ _logger = logger;
+ _json = json;
+ _appPaths = appPaths;
+ }
+
+ public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, CancellationToken cancellationToken)
+ {
+ var originalRuntime = mediaSource.RunTimeTicks;
+
+ var now = DateTime.UtcNow;
+
+ MediaInfo mediaInfo = null;
+ var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json");
+
+ if (!string.IsNullOrEmpty(cacheKey))
+ {
+ try
+ {
+ mediaInfo = _json.DeserializeFromFile<MediaInfo>(cacheFilePath);
+
+ //_logger.Debug("Found cached media info");
+ }
+ catch
+ {
+ }
+ }
+
+ if (mediaInfo == null)
+ {
+ if (addProbeDelay)
+ {
+ var delayMs = mediaSource.AnalyzeDurationMs ?? 0;
+ delayMs = Math.Max(3000, delayMs);
+ if (delayMs > 0)
+ {
+ _logger.Info("Waiting {0}ms before probing the live stream", delayMs);
+ await Task.Delay(delayMs, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ mediaSource.AnalyzeDurationMs = 3000;
+
+ mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ {
+ MediaSource = mediaSource,
+ MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
+ ExtractChapters = false
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ if (cacheFilePath != null)
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
+ _json.SerializeToFile(mediaInfo, cacheFilePath);
+
+ //_logger.Debug("Saved media info to {0}", cacheFilePath);
+ }
+ }
+
+ var mediaStreams = mediaInfo.MediaStreams;
+
+ if (!string.IsNullOrEmpty(cacheKey))
+ {
+ var newList = new List<MediaStream>();
+ newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Video).Take(1));
+ newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Take(1));
+
+ foreach (var stream in newList)
+ {
+ stream.Index = -1;
+ stream.Language = null;
+ }
+
+ mediaStreams = newList;
+ }
+
+ _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+
+ mediaSource.Bitrate = mediaInfo.Bitrate;
+ mediaSource.Container = mediaInfo.Container;
+ mediaSource.Formats = mediaInfo.Formats;
+ mediaSource.MediaStreams = mediaStreams;
+ mediaSource.RunTimeTicks = mediaInfo.RunTimeTicks;
+ mediaSource.Size = mediaInfo.Size;
+ mediaSource.Timestamp = mediaInfo.Timestamp;
+ mediaSource.Video3DFormat = mediaInfo.Video3DFormat;
+ mediaSource.VideoType = mediaInfo.VideoType;
+
+ mediaSource.DefaultSubtitleStreamIndex = null;
+
+ // Null this out so that it will be treated like a live stream
+ if (!originalRuntime.HasValue)
+ {
+ mediaSource.RunTimeTicks = null;
+ }
+
+ var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
+
+ if (audioStream == null || audioStream.Index == -1)
+ {
+ mediaSource.DefaultAudioStreamIndex = null;
+ }
+ else
+ {
+ mediaSource.DefaultAudioStreamIndex = audioStream.Index;
+ }
+
+ var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
+ if (videoStream != null)
+ {
+ if (!videoStream.BitRate.HasValue)
+ {
+ var width = videoStream.Width ?? 1920;
+
+ if (width >= 3000)
+ {
+ videoStream.BitRate = 30000000;
+ }
+
+ else if (width >= 1900)
+ {
+ videoStream.BitRate = 20000000;
+ }
+
+ else if (width >= 1200)
+ {
+ videoStream.BitRate = 8000000;
+ }
+
+ else if (width >= 700)
+ {
+ videoStream.BitRate = 2000000;
+ }
+ }
+
+ // This is coming up false and preventing stream copy
+ videoStream.IsAVC = null;
+ }
+
+ mediaSource.AnalyzeDurationMs = 3000;
+
+ // Try to estimate this
+ mediaSource.InferTotalBitrate(true);
+ }
+
+ public Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, bool addProbeDelay, CancellationToken cancellationToken)
+ {
+ return AddMediaInfoWithProbe(mediaSource, isAudio, null, addProbeDelay, cancellationToken);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
deleted file mode 100644
index 4830da8fc..000000000
--- a/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using MediaBrowser.Controller.Channels;
-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;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-
-namespace Emby.Server.Implementations.Library
-{
- public class LocalTrailerPostScanTask : ILibraryPostScanTask
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IChannelManager _channelManager;
-
- public LocalTrailerPostScanTask(ILibraryManager libraryManager, IChannelManager channelManager)
- {
- _libraryManager = libraryManager;
- _channelManager = channelManager;
- }
-
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var items = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(BoxSet).Name, typeof(Game).Name, typeof(Movie).Name, typeof(Series).Name },
- Recursive = true,
- DtoOptions = new DtoOptions(true)
-
- }).OfType<IHasTrailers>().ToList();
-
- var trailerTypes = Enum.GetNames(typeof(TrailerType))
- .Select(i => (TrailerType)Enum.Parse(typeof(TrailerType), i, true))
- .Except(new[] { TrailerType.LocalTrailer })
- .ToArray();
-
- var trailers = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Trailer).Name },
- TrailerTypes = trailerTypes,
- Recursive = true,
- DtoOptions = new DtoOptions(false)
-
- });
-
- var numComplete = 0;
-
- foreach (var item in items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- AssignTrailers(item, trailers);
-
- numComplete++;
- double percent = numComplete;
- percent /= items.Count;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private void AssignTrailers(IHasTrailers item, IEnumerable<BaseItem> channelTrailers)
- {
- if (item is Game)
- {
- return;
- }
-
- var imdbId = item.GetProviderId(MetadataProviders.Imdb);
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
-
- var trailers = channelTrailers.Where(i =>
- {
- if (!string.IsNullOrWhiteSpace(imdbId) &&
- string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- if (!string.IsNullOrWhiteSpace(tmdbId) &&
- string.Equals(tmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- return false;
- });
-
- var trailerIds = trailers.Select(i => i.Id)
- .ToArray();
-
- if (!trailerIds.SequenceEqual(item.RemoteTrailerIds))
- {
- item.RemoteTrailerIds = trailerIds;
-
- var baseItem = (BaseItem)item;
- baseItem.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 688da5764..0dc436800 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
@@ -16,6 +17,11 @@ using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using System.IO;
+using System.Globalization;
+using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.Library
{
@@ -31,8 +37,11 @@ namespace Emby.Server.Implementations.Library
private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager;
private readonly ITimerFactory _timerFactory;
+ private readonly Func<IMediaEncoder> _mediaEncoder;
+ private ILocalizationManager _localizationManager;
+ private IApplicationPaths _appPaths;
- public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager, ITimerFactory timerFactory)
+ public MediaSourceManager(IItemRepository itemRepo, IApplicationPaths applicationPaths, ILocalizationManager localizationManager, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager, ITimerFactory timerFactory, Func<IMediaEncoder> mediaEncoder)
{
_itemRepo = itemRepo;
_userManager = userManager;
@@ -42,6 +51,9 @@ namespace Emby.Server.Implementations.Library
_fileSystem = fileSystem;
_userDataManager = userDataManager;
_timerFactory = timerFactory;
+ _mediaEncoder = mediaEncoder;
+ _localizationManager = localizationManager;
+ _appPaths = applicationPaths;
}
public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@@ -109,20 +121,23 @@ namespace Emby.Server.Implementations.Library
return streams;
}
- public async Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken)
+ public async Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
- var item = _libraryManager.GetItemById(id);
+ var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
- var hasMediaSources = (IHasMediaSources)item;
- User user = null;
-
- if (!string.IsNullOrWhiteSpace(userId))
+ if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video))
{
- user = _userManager.GetUserById(userId);
+ await item.RefreshMetadata(new MediaBrowser.Controller.Providers.MetadataRefreshOptions(_fileSystem)
+ {
+ EnableRemoteContentProbe = true,
+ MetadataRefreshMode = MediaBrowser.Controller.Providers.MetadataRefreshMode.FullRefresh
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
}
- var mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
- var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false);
+ var dynamicMediaSources = await GetDynamicMediaSources(item, cancellationToken).ConfigureAwait(false);
var list = new List<MediaSourceInfo>();
@@ -132,24 +147,13 @@ namespace Emby.Server.Implementations.Library
{
if (user != null)
{
- SetUserProperties(hasMediaSources, source, user);
- }
- if (source.Protocol == MediaProtocol.File)
- {
- // TODO: Path substitution
- if (!_fileSystem.FileExists(source.Path))
- {
- source.SupportsDirectStream = false;
- }
- }
- else if (source.Protocol == MediaProtocol.Http)
- {
- // TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash
- source.SupportsDirectStream = false;
+ SetDefaultAudioAndSubtitleStreamIndexes(item, source, user);
}
- else
+
+ // Validate that this is actually possible
+ if (source.SupportsDirectStream)
{
- source.SupportsDirectStream = false;
+ source.SupportsDirectStream = SupportsDirectStream(source.Path, source.Protocol);
}
list.Add(source);
@@ -169,10 +173,63 @@ namespace Emby.Server.Implementations.Library
}
}
- return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder);
+ return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder).ToList();
+ }
+
+ public MediaProtocol GetPathProtocol(string path)
+ {
+ if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Rtsp;
+ }
+ if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Rtmp;
+ }
+ if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Http;
+ }
+ if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Rtp;
+ }
+ if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Ftp;
+ }
+ if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Udp;
+ }
+
+ return _fileSystem.IsPathFile(path) ? MediaProtocol.File : MediaProtocol.Http;
}
- private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
+ public bool SupportsDirectStream(string path, MediaProtocol protocol)
+ {
+ if (protocol == MediaProtocol.File)
+ {
+ return true;
+ }
+
+ if (protocol == MediaProtocol.Http)
+ {
+ if (path != null)
+ {
+ if (path.IndexOf(".m3u", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken)
{
var tasks = _providers.Select(i => GetDynamicMediaSources(item, i, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -180,7 +237,7 @@ namespace Emby.Server.Implementations.Library
return results.SelectMany(i => i.ToList());
}
- private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, IMediaSourceProvider provider, CancellationToken cancellationToken)
+ private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, IMediaSourceProvider provider, CancellationToken cancellationToken)
{
try
{
@@ -207,78 +264,65 @@ namespace Emby.Server.Implementations.Library
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter;
- if (!string.IsNullOrWhiteSpace(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.OpenToken = prefix + mediaSource.OpenToken;
}
- if (!string.IsNullOrWhiteSpace(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.LiveStreamId = prefix + mediaSource.LiveStreamId;
}
}
- public async Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken)
+ public async Task<MediaSourceInfo> GetMediaSource(BaseItem item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken)
{
- if (!string.IsNullOrWhiteSpace(liveStreamId))
+ if (!string.IsNullOrEmpty(liveStreamId))
{
return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
}
- //await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- //try
- //{
- // var stream = _openStreams.Values.FirstOrDefault(i => string.Equals(i.MediaSource.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
- // if (stream != null)
- // {
- // return stream.MediaSource;
- // }
- //}
- //finally
- //{
- // _liveStreamSemaphore.Release();
- //}
-
- var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video },
- CancellationToken.None).ConfigureAwait(false);
+ var sources = await GetPlayackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
- public List<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null)
+ public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
- if (!(item is Video))
- {
- return item.GetMediaSources(enablePathSubstitution);
- }
+ var hasMediaSources = (IHasMediaSources)item;
- var sources = item.GetMediaSources(enablePathSubstitution);
+ var sources = hasMediaSources.GetMediaSources(enablePathSubstitution);
if (user != null)
{
foreach (var source in sources)
{
- SetUserProperties(item, source, user);
+ SetDefaultAudioAndSubtitleStreamIndexes(item, source, user);
}
}
return sources;
}
- private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user)
+ private string[] NormalizeLanguage(string language)
{
- var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item);
+ if (language != null)
+ {
+ var culture = _localizationManager.FindLanguageInfo(language);
+ if (culture != null)
+ {
+ return culture.ThreeLetterISOLanguageNames;
+ }
- var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections;
+ return new string[] { language };
+ }
- SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
- SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
+ return Array.Empty<string>();
}
private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
@@ -293,9 +337,9 @@ namespace Emby.Server.Implementations.Library
return;
}
}
-
+
var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? new List<string>() : new List<string> { user.Configuration.SubtitleLanguagePreference };
+ ? Array.Empty<string>() : NormalizeLanguage(user.Configuration.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null
@@ -325,12 +369,37 @@ namespace Emby.Server.Implementations.Library
}
var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
- ? new string[] { }
- : new[] { user.Configuration.AudioLanguagePreference };
+ ? Array.Empty<string>()
+ : NormalizeLanguage(user.Configuration.AudioLanguagePreference);
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
}
+ public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
+ {
+ // Item would only be null if the app didn't supply ItemId as part of the live stream open request
+ var mediaType = item == null ? MediaType.Video : item.MediaType;
+
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item);
+
+ var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections;
+
+ SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
+ SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
+ }
+ else if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ var audio = source.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+
+ if (audio != null)
+ {
+ source.DefaultAudioStreamIndex = audio.Index;
+ }
+ }
+ }
+
private IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
{
return sources.OrderBy(i =>
@@ -352,55 +421,157 @@ namespace Emby.Server.Implementations.Library
.ToList();
}
- private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
- public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
+ public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+ MediaSourceInfo mediaSource;
+ ILiveStream liveStream;
+
try
{
var tuple = GetProvider(request.OpenToken);
var provider = tuple.Item1;
- var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, request.EnableMediaProbe, cancellationToken).ConfigureAwait(false);
+ var currentLiveStreams = _openStreams.Values.ToList();
+
+ liveStream = await provider.OpenMediaSource(tuple.Item2, currentLiveStreams, cancellationToken).ConfigureAwait(false);
- var mediaSource = mediaSourceTuple.Item1;
+ mediaSource = liveStream.MediaSource;
- if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
+ // Validate that this is actually possible
+ if (mediaSource.SupportsDirectStream)
{
- throw new InvalidOperationException(string.Format("{0} returned null LiveStreamId", provider.GetType().Name));
+ mediaSource.SupportsDirectStream = SupportsDirectStream(mediaSource.Path, mediaSource.Protocol);
}
SetKeyProperties(provider, mediaSource);
- var info = new LiveStreamInfo
+ _openStreams[mediaSource.LiveStreamId] = liveStream;
+ }
+ finally
+ {
+ _liveStreamSemaphore.Release();
+ }
+
+ // TODO: Don't hardcode this
+ var isAudio = false;
+
+ try
+ {
+ if (mediaSource.MediaStreams.Any(i => i.Index != -1) || !mediaSource.SupportsProbing)
{
- Id = mediaSource.LiveStreamId,
- MediaSource = mediaSource,
- DirectStreamProvider = mediaSourceTuple.Item2
- };
-
- _openStreams[mediaSource.LiveStreamId] = info;
-
- var json = _jsonSerializer.SerializeToString(mediaSource);
- _logger.Debug("Live stream opened: " + json);
- var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
-
- if (!string.IsNullOrWhiteSpace(request.UserId))
- {
- var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrWhiteSpace(request.ItemId)
- ? null
- : _libraryManager.GetItemById(request.ItemId);
- SetUserProperties(item, clone, user);
+ AddMediaInfo(mediaSource, isAudio);
+ }
+ else
+ {
+ // hack - these two values were taken from LiveTVMediaSourceProvider
+ var cacheKey = request.OpenToken;
+
+ await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths).AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken).ConfigureAwait(false);
}
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error probing live tv stream", ex);
+ AddMediaInfo(mediaSource, isAudio);
+ }
+
+ var json = _jsonSerializer.SerializeToString(mediaSource);
+ _logger.Info("Live stream opened: " + json);
+ var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
+
+ if (!request.UserId.Equals(Guid.Empty))
+ {
+ var user = _userManager.GetUserById(request.UserId);
+ var item = request.ItemId.Equals(Guid.Empty)
+ ? null
+ : _libraryManager.GetItemById(request.ItemId);
+ SetDefaultAudioAndSubtitleStreamIndexes(item, clone, user);
+ }
+
+ return new Tuple<LiveStreamResponse, IDirectStreamProvider>(new LiveStreamResponse
+ {
+ MediaSource = clone
+
+ }, liveStream as IDirectStreamProvider);
+ }
+
+ private void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio)
+ {
+ mediaSource.DefaultSubtitleStreamIndex = null;
+
+ // Null this out so that it will be treated like a live stream
+ if (mediaSource.IsInfiniteStream)
+ {
+ mediaSource.RunTimeTicks = null;
+ }
- return new LiveStreamResponse
+ var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
+
+ if (audioStream == null || audioStream.Index == -1)
+ {
+ mediaSource.DefaultAudioStreamIndex = null;
+ }
+ else
+ {
+ mediaSource.DefaultAudioStreamIndex = audioStream.Index;
+ }
+
+ var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
+ if (videoStream != null)
+ {
+ if (!videoStream.BitRate.HasValue)
{
- MediaSource = clone
- };
+ var width = videoStream.Width ?? 1920;
+
+ if (width >= 3000)
+ {
+ videoStream.BitRate = 30000000;
+ }
+
+ else if (width >= 1900)
+ {
+ videoStream.BitRate = 20000000;
+ }
+
+ else if (width >= 1200)
+ {
+ videoStream.BitRate = 8000000;
+ }
+
+ else if (width >= 700)
+ {
+ videoStream.BitRate = 2000000;
+ }
+ }
+ }
+
+ // Try to estimate this
+ mediaSource.InferTotalBitrate();
+ }
+
+ public async Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
+ {
+ await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ var info = _openStreams.Values.FirstOrDefault(i =>
+ {
+ var liveStream = i as ILiveStream;
+ if (liveStream != null)
+ {
+ return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+ });
+
+ return info as IDirectStreamProvider;
}
finally
{
@@ -408,23 +579,207 @@ namespace Emby.Server.Implementations.Library
}
}
+ public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
+ {
+ var result = await OpenLiveStreamInternal(request, cancellationToken).ConfigureAwait(false);
+ return result.Item1;
+ }
+
+ public async Task<MediaSourceInfo> GetLiveStreamMediaInfo(string id, CancellationToken cancellationToken)
+ {
+ var liveStreamInfo = await GetLiveStreamInfo(id, cancellationToken).ConfigureAwait(false);
+
+ var mediaSource = liveStreamInfo.MediaSource;
+
+ if (liveStreamInfo is IDirectStreamProvider)
+ {
+ var info = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+ {
+ MediaSource = mediaSource,
+ ExtractChapters = false,
+ MediaType = DlnaProfileType.Video
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ mediaSource.MediaStreams = info.MediaStreams;
+ mediaSource.Container = info.Container;
+ mediaSource.Bitrate = info.Bitrate;
+ }
+
+ return mediaSource;
+ }
+
+ public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, bool isLiveStream, CancellationToken cancellationToken)
+ {
+ var originalRuntime = mediaSource.RunTimeTicks;
+
+ var now = DateTime.UtcNow;
+
+ MediaInfo mediaInfo = null;
+ var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json");
+
+ if (!string.IsNullOrEmpty(cacheKey))
+ {
+ try
+ {
+ mediaInfo = _jsonSerializer.DeserializeFromFile<MediaInfo>(cacheFilePath);
+
+ //_logger.Debug("Found cached media info");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ if (mediaInfo == null)
+ {
+ if (addProbeDelay)
+ {
+ var delayMs = mediaSource.AnalyzeDurationMs ?? 0;
+ delayMs = Math.Max(3000, delayMs);
+ await Task.Delay(delayMs, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (isLiveStream)
+ {
+ mediaSource.AnalyzeDurationMs = 3000;
+ }
+
+ mediaInfo = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+ {
+ MediaSource = mediaSource,
+ MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
+ ExtractChapters = false
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ if (cacheFilePath != null)
+ {
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
+ _jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath);
+
+ //_logger.Debug("Saved media info to {0}", cacheFilePath);
+ }
+ }
+
+ var mediaStreams = mediaInfo.MediaStreams;
+
+ if (isLiveStream && !string.IsNullOrEmpty(cacheKey))
+ {
+ var newList = new List<MediaStream>();
+ newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Video).Take(1));
+ newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Take(1));
+
+ foreach (var stream in newList)
+ {
+ stream.Index = -1;
+ stream.Language = null;
+ }
+
+ mediaStreams = newList;
+ }
+
+ _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+
+ mediaSource.Bitrate = mediaInfo.Bitrate;
+ mediaSource.Container = mediaInfo.Container;
+ mediaSource.Formats = mediaInfo.Formats;
+ mediaSource.MediaStreams = mediaStreams;
+ mediaSource.RunTimeTicks = mediaInfo.RunTimeTicks;
+ mediaSource.Size = mediaInfo.Size;
+ mediaSource.Timestamp = mediaInfo.Timestamp;
+ mediaSource.Video3DFormat = mediaInfo.Video3DFormat;
+ mediaSource.VideoType = mediaInfo.VideoType;
+
+ mediaSource.DefaultSubtitleStreamIndex = null;
+
+ if (isLiveStream)
+ {
+ // Null this out so that it will be treated like a live stream
+ if (!originalRuntime.HasValue)
+ {
+ mediaSource.RunTimeTicks = null;
+ }
+ }
+
+ var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+
+ if (audioStream == null || audioStream.Index == -1)
+ {
+ mediaSource.DefaultAudioStreamIndex = null;
+ }
+ else
+ {
+ mediaSource.DefaultAudioStreamIndex = audioStream.Index;
+ }
+
+ var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ if (videoStream != null)
+ {
+ if (!videoStream.BitRate.HasValue)
+ {
+ var width = videoStream.Width ?? 1920;
+
+ if (width >= 3000)
+ {
+ videoStream.BitRate = 30000000;
+ }
+
+ else if (width >= 1900)
+ {
+ videoStream.BitRate = 20000000;
+ }
+
+ else if (width >= 1200)
+ {
+ videoStream.BitRate = 8000000;
+ }
+
+ else if (width >= 700)
+ {
+ videoStream.BitRate = 2000000;
+ }
+ }
+
+ // This is coming up false and preventing stream copy
+ videoStream.IsAVC = null;
+ }
+
+ if (isLiveStream)
+ {
+ mediaSource.AnalyzeDurationMs = 3000;
+ }
+
+ // Try to estimate this
+ mediaSource.InferTotalBitrate(true);
+ }
+
public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(id))
+ if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
- _logger.Debug("Getting already opened live stream {0}", id);
+ var info = await GetLiveStreamInfo(id, cancellationToken).ConfigureAwait(false);
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider);
+ }
+
+ private async Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
- LiveStreamInfo info;
+ ILiveStream info;
if (_openStreams.TryGetValue(id, out info))
{
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info.DirectStreamProvider);
+ return info;
}
else
{
@@ -443,26 +798,9 @@ namespace Emby.Server.Implementations.Library
return result.Item1;
}
- private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId)
- {
- _logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name);
-
- try
- {
- await provider.CloseMediaSource(streamId).ConfigureAwait(false);
- }
- catch (NotImplementedException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream {0}", ex, streamId);
- }
- }
-
public async Task CloseLiveStream(string id)
{
- if (string.IsNullOrWhiteSpace(id))
+ if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
@@ -471,18 +809,22 @@ namespace Emby.Server.Implementations.Library
try
{
- LiveStreamInfo current;
+ ILiveStream liveStream;
- if (_openStreams.TryGetValue(id, out current))
+ if (_openStreams.TryGetValue(id, out liveStream))
{
- _openStreams.Remove(id);
- current.Closed = true;
+ liveStream.ConsumerCount--;
- if (current.MediaSource.RequiresClosing)
+ _logger.Info("Live stream {0} consumer count is now {1}", liveStream.OriginalStreamId, liveStream.ConsumerCount);
+
+ if (liveStream.ConsumerCount <= 0)
{
- var tuple = GetProvider(id);
+ _openStreams.Remove(id);
+
+ _logger.Info("Closing live stream {0}", id);
- await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
+ await liveStream.Close().ConfigureAwait(false);
+ _logger.Info("Live stream {0} closed successfully", id);
}
}
}
@@ -497,7 +839,7 @@ namespace Emby.Server.Implementations.Library
private Tuple<IMediaSourceProvider, string> GetProvider(string key)
{
- if (string.IsNullOrWhiteSpace(key))
+ if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("key");
}
@@ -518,7 +860,6 @@ namespace Emby.Server.Implementations.Library
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
private readonly object _disposeLock = new object();
@@ -541,13 +882,5 @@ namespace Emby.Server.Implementations.Library
}
}
}
-
- private class LiveStreamInfo
- {
- public string Id;
- public bool Closed;
- public MediaSourceInfo MediaSource;
- public IDirectStreamProvider DirectStreamProvider;
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index 8169cc7d9..5d4c5a452 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -4,13 +4,13 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Controller.MediaEncoding
+namespace Emby.Server.Implementations.Library
{
public static class MediaStreamSelector
{
- public static int? GetDefaultAudioStreamIndex(List<MediaStream> streams, IEnumerable<string> preferredLanguages, bool preferDefaultTrack)
+ public static int? GetDefaultAudioStreamIndex(List<MediaStream> streams, string[] preferredLanguages, bool preferDefaultTrack)
{
- streams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages.ToList())
+ streams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages)
.ToList();
if (preferDefaultTrack)
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams,
- List<string> preferredLanguages,
+ string[] preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
@@ -57,9 +57,9 @@ namespace MediaBrowser.Controller.MediaEncoding
streams.FirstOrDefault(s => s.IsDefault);
// if the audio language is not understood by the user, load their preferred subs, if there are any
- if (stream == null && !ContainsOrdinal(preferredLanguages, audioTrackLanguage))
+ if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
{
- stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
+ stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
}
}
else if (mode == SubtitlePlaybackMode.Smart)
@@ -67,10 +67,10 @@ namespace MediaBrowser.Controller.MediaEncoding
// Prefer smart logic over embedded metadata
// if the audio language is not understood by the user, load their preferred subs, if there are any
- if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
+ if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
{
- stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)) ??
- streams.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
+ stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)) ??
+ streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase));
}
}
else if (mode == SubtitlePlaybackMode.Always)
@@ -96,18 +96,13 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
- private static bool ContainsOrdinal(IEnumerable<string> list, string item)
- {
- return list.Any(i => string.Equals(i, item, StringComparison.OrdinalIgnoreCase));
- }
-
- private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, List<string> languagePreferences)
+ private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, string[] languagePreferences)
{
// Give some preferance to external text subs for better performance
return streams.Where(i => i.Type == type)
.OrderBy(i =>
{
- var index = languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase));
+ var index = FindIndex(languagePreferences, i.Language);
return index == -1 ? 100 : index;
})
@@ -119,7 +114,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public static void SetSubtitleStreamScores(List<MediaStream> streams,
- List<string> preferredLanguages,
+ string[] preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
@@ -136,18 +131,15 @@ namespace MediaBrowser.Controller.MediaEncoding
if (mode == SubtitlePlaybackMode.Default)
{
// Prefer embedded metadata over smart logic
-
filteredStreams = streams.Where(s => s.IsForced || s.IsDefault)
.ToList();
}
else if (mode == SubtitlePlaybackMode.Smart)
{
// Prefer smart logic over embedded metadata
-
- // if the audio language is not understood by the user, load their preferred subs, if there are any
- if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
+ if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase))
{
- filteredStreams = streams.Where(s => !s.IsForced && ContainsOrdinal(preferredLanguages, s.Language))
+ filteredStreams = streams.Where(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase))
.ToList();
}
}
@@ -177,11 +169,24 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- private static int GetSubtitleScore(MediaStream stream, List<string> languagePreferences)
+ private static int FindIndex(string[] list, string value)
+ {
+ for (var i=0; i< list.Length; i++)
+ {
+ if (string.Equals(list[i], value, StringComparison.OrdinalIgnoreCase))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ private static int GetSubtitleScore(MediaStream stream, string[] languagePreferences)
{
var values = new List<int>();
- var index = languagePreferences.FindIndex(l => string.Equals(stream.Language, l, StringComparison.OrdinalIgnoreCase));
+ var index = FindIndex(languagePreferences, stream.Language);
values.Add(index == -1 ? 0 : 100 - index);
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 1cbf4235a..1319ee6f4 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -67,19 +67,19 @@ namespace Emby.Server.Implementations.Library
{
try
{
- return _libraryManager.GetMusicGenre(i).Id.ToString("N");
+ return _libraryManager.GetMusicGenre(i).Id;
}
catch
{
- return null;
+ return Guid.Empty;
}
- }).Where(i => i != null);
+ }).Where(i => !i.Equals(Guid.Empty)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
- public List<BaseItem> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user, DtoOptions dtoOptions)
+ public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Library
Limit = 200,
- OrderBy = new [] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
+ OrderBy = new [] { new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
DtoOptions = dtoOptions
@@ -101,7 +101,7 @@ namespace Emby.Server.Implementations.Library
var genre = item as MusicGenre;
if (genre != null)
{
- return GetInstantMixFromGenreIds(new[] { item.Id.ToString("N") }, user, dtoOptions);
+ return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
}
var playlist = item as Playlist;
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index d0096de0c..14b28966a 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Library
public static void SetInitialItemValues(BaseItem item, Folder parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService)
{
// This version of the below method has no ItemResolveArgs, so we have to require the path already being set
- if (string.IsNullOrWhiteSpace(item.Path))
+ if (string.IsNullOrEmpty(item.Path))
{
throw new ArgumentException("Item must have a Path");
}
@@ -108,17 +108,6 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// The MB name regex
- /// </summary>
- private static readonly Regex MbNameRegex = new Regex(@"(\[.*?\])");
-
- internal static string StripBrackets(string inputString)
- {
- var output = MbNameRegex.Replace(inputString, string.Empty).Trim();
- return Regex.Replace(output, @"\s+", " ");
- }
-
- /// <summary>
/// Ensures DateCreated and DateModified have values
/// </summary>
/// <param name="fileSystem">The file system.</param>
@@ -140,7 +129,7 @@ namespace Emby.Server.Implementations.Library
}
// See if a different path came out of the resolver than what went in
- if (!string.Equals(args.Path, item.Path, StringComparison.OrdinalIgnoreCase))
+ if (!fileSystem.AreEqual(args.Path, item.Path))
{
var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
@@ -173,7 +162,14 @@ namespace Emby.Server.Implementations.Library
// directoryService.getFile may return null
if (info != null)
{
- item.DateCreated = fileSystem.GetCreationTimeUtc(info);
+ var dateCreated = fileSystem.GetCreationTimeUtc(info);
+
+ if (dateCreated.Equals(DateTime.MinValue))
+ {
+ dateCreated = DateTime.UtcNow;
+ }
+
+ item.DateCreated = dateCreated;
}
}
else
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index d30aaa133..8872bd641 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -101,13 +101,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (LibraryManager.IsAudioFile(args.Path, libraryOptions))
{
- if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase))
+ var extension = Path.GetExtension(args.Path);
+
+ if (string.Equals(extension, ".cue", StringComparison.OrdinalIgnoreCase))
{
// if audio file exists of same name, return null
return null;
}
- var isMixedCollectionType = string.IsNullOrWhiteSpace(collectionType);
+ var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
// For conflicting extensions, give priority to videos
if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path, libraryOptions))
@@ -134,6 +136,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (item != null)
{
+ item.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
+
item.IsInMixedFolder = true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index b8ec41805..a33f101ae 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -52,14 +52,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <returns>MusicAlbum.</returns>
protected override MusicAlbum Resolve(ItemResolveArgs args)
{
- if (!args.IsDirectory) return null;
-
- // Avoid mis-identifying top folders
- if (args.HasParent<MusicAlbum>()) return null;
- if (args.Parent.IsRoot) return null;
-
var collectionType = args.GetCollectionType();
-
var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
// If there's a collection type and it's not music, don't allow it.
@@ -68,6 +61,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
+ if (!args.IsDirectory) return null;
+
+ // Avoid mis-identifying top folders
+ if (args.HasParent<MusicAlbum>()) return null;
+ if (args.Parent.IsRoot) return null;
+
return IsMusicAlbum(args) ? new MusicAlbum() : null;
}
@@ -117,24 +116,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
if (allowSubfolders)
{
+ if (notMultiDisc)
+ {
+ continue;
+ }
+
var path = fileSystemInfo.FullName;
- var isMultiDisc = IsMultiDiscFolder(path, libraryOptions);
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
- if (isMultiDisc)
+ if (hasMusic)
{
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
-
- if (hasMusic)
+ if (IsMultiDiscFolder(path, libraryOptions))
{
logger.Debug("Found multi-disc folder: " + path);
discSubfolderCount++;
}
- }
- else
- {
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
-
- if (hasMusic)
+ else
{
// If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
notMultiDisc = true;
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 7e960f85e..556748183 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -184,11 +184,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
else if (string.Equals(videoInfo.StubType, "bluray", StringComparison.OrdinalIgnoreCase))
{
video.VideoType = VideoType.BluRay;
- video.IsHD = true;
- }
- else if (string.Equals(videoInfo.StubType, "hdtv", StringComparison.OrdinalIgnoreCase))
- {
- video.IsHD = true;
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
index df441c5ed..b9aca1417 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using System;
using System.IO;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
@@ -30,14 +31,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
return null;
}
-
- if (filename.IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1 ||
- args.ContainsFileSystemEntryByName("collection.xml"))
+
+ if (filename.IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1 || args.ContainsFileSystemEntryByName("collection.xml"))
{
return new BoxSet
{
Path = args.Path,
- Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path))
+ Name = Path.GetFileName(args.Path).Replace("[boxset]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
};
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index d74235ec7..1394e3858 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return ResolveVideos<Video>(parent, files, directoryService, false, collectionType, false);
}
- if (string.IsNullOrWhiteSpace(collectionType))
+ if (string.IsNullOrEmpty(collectionType))
{
// Owned items should just use the plain video type
if (parent == null)
@@ -113,7 +113,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
foreach (var child in fileSystemEntries)
{
// This is a hack but currently no better way to resolve a sometimes ambiguous situation
- if (string.IsNullOrWhiteSpace(collectionType))
+ if (string.IsNullOrEmpty(collectionType))
{
if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) ||
string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
@@ -126,6 +126,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
leftOver.Add(child);
}
+ else if (IsIgnored(child.Name))
+ {
+
+ }
else
{
files.Add(child);
@@ -172,6 +176,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return result;
}
+ private bool IsIgnored(string filename)
+ {
+ // Ignore samples
+ var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
+ .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
+
+ if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
private bool ContainsFile(List<VideoInfo> result, FileSystemMetadata file)
{
return result.Any(i => ContainsFile(i, file));
@@ -317,7 +337,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
//we need to only look at the name of this actual item (not parents)
var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath);
- if (!string.IsNullOrWhiteSpace(justName))
+ if (!string.IsNullOrEmpty(justName))
{
// check for tmdb id
var tmdbid = justName.GetAttributeValue("tmdbid");
@@ -328,7 +348,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
}
- if (!string.IsNullOrWhiteSpace(item.Path))
+ if (!string.IsNullOrEmpty(item.Path))
{
// check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name)
var imdbid = item.Path.GetAttributeValue("imdbid");
@@ -395,16 +415,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
Set3DFormat(movie);
return movie;
}
- else if (supportPhotos && !child.IsHidden && PhotoResolver.IsImageFile(child.FullName, _imageProcessor))
+ else if (supportPhotos && PhotoResolver.IsImageFile(child.FullName, _imageProcessor))
{
photos.Add(child);
}
}
// TODO: Allow GetMultiDiscMovie in here
- var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) &&
- !string.Equals(collectionType, CollectionType.Photos) &&
- !string.Equals(collectionType, CollectionType.MusicVideos);
+ var supportsMultiVersion = true;
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
new MultiItemResolverResult();
@@ -532,7 +550,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
}
- if (string.IsNullOrWhiteSpace(collectionType))
+ if (string.IsNullOrEmpty(collectionType))
{
return false;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index 48f5802a9..e3cce5f4b 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -94,7 +94,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
"backdrop",
"poster",
"cover",
- "logo"
+ "logo",
+ "default"
};
internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index 8c59cf20f..e66c9f087 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -2,11 +2,20 @@
using MediaBrowser.Controller.Playlists;
using System;
using System.IO;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Entities;
+using System.Linq;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class PlaylistResolver : FolderResolver<Playlist>
{
+ private string[] SupportedCollectionTypes = new string[] {
+
+ string.Empty,
+ CollectionType.Music
+ };
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -31,10 +40,26 @@ namespace Emby.Server.Implementations.Library.Resolvers
return new Playlist
{
Path = args.Path,
- Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path))
+ Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
};
}
}
+ else
+ {
+ if (SupportedCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ var extension = Path.GetExtension(args.Path);
+ if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return new Playlist
+ {
+ Path = args.Path,
+ Name = Path.GetFileNameWithoutExtension(args.Path),
+ IsInMixedFolder = true
+ };
+ }
+ }
+ }
return null;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 3bad69b56..d8343f7c6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -50,24 +50,29 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var path = args.Path;
+ var seasonParserResult = new SeasonPathParser(namingOptions).Parse(path, true, true);
+
var season = new Season
{
- IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(path, true, true).SeasonNumber,
+ IndexNumber = seasonParserResult.SeasonNumber,
SeriesId = series.Id,
SeriesName = series.Name
};
- if (season.IndexNumber.HasValue)
+ if (!season.IndexNumber.HasValue || !seasonParserResult.IsSeasonFolder)
{
var resolver = new Emby.Naming.TV.EpisodeResolver(namingOptions);
- var episodeInfo = resolver.Resolve(path, true);
+ var folderName = System.IO.Path.GetFileName(path);
+ var testPath = "\\\\test\\" + folderName;
+
+ var episodeInfo = resolver.Resolve(testPath, true);
if (episodeInfo != null)
{
if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
{
- _logger.Info("Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
+ _logger.Debug("Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
path,
episodeInfo.SeasonNumber.Value,
episodeInfo.EpisodeNumber.Value);
@@ -75,7 +80,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
}
+ }
+ if (season.IndexNumber.HasValue)
+ {
var seasonNumber = season.IndexNumber.Value;
season.Name = seasonNumber == 0 ?
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index a693e3b26..951f439c2 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -82,11 +82,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
};
}
}
- else if (string.IsNullOrWhiteSpace(collectionType))
+ else if (string.IsNullOrEmpty(collectionType))
{
if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
{
- if (args.Parent.IsRoot)
+ if (args.Parent != null && args.Parent.IsRoot)
{
// For now, return null, but if we want to allow this in the future then add some additional checks to guard against a misplaced tvshow.nfo
return null;
@@ -99,7 +99,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
};
}
- if (args.Parent.IsRoot)
+ if (args.Parent != null && args.Parent.IsRoot)
{
return null;
}
@@ -160,11 +160,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return true;
}
- var allowOptimisticEpisodeDetection = isTvContentType;
- var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(allowOptimisticEpisodeDetection);
+ var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
var episodeResolver = new Emby.Naming.TV.EpisodeResolver(namingOptions);
- var episodeInfo = episodeResolver.Resolve(fullName, false, false);
+ bool? isNamed = null;
+ bool? isOptimistic = null;
+
+ if (!isTvContentType)
+ {
+ isNamed = true;
+ isOptimistic = false;
+ }
+
+ var episodeInfo = episodeResolver.Resolve(fullName, false, isNamed, isOptimistic, null, false);
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
{
return true;
@@ -206,7 +214,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
- var seasonNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(path, isTvContentType, isTvContentType).SeasonNumber;
+ var seasonNumber = new SeasonPathParser(namingOptions).Parse(path, isTvContentType, isTvContentType).SeasonNumber;
return seasonNumber.HasValue;
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 8021399bd..7f04ac5bc 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -28,14 +28,14 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager;
_userManager = userManager;
- _logger = logManager.GetLogger("Lucene");
+ _logger = logManager.GetLogger("SearchEngine");
}
- public async Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query)
+ public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
{
User user = null;
- if (string.IsNullOrWhiteSpace(query.UserId))
+ if (query.UserId.Equals(Guid.Empty))
{
}
else
@@ -43,26 +43,22 @@ namespace Emby.Server.Implementations.Library
user = _userManager.GetUserById(query.UserId);
}
- var results = await GetSearchHints(query, user).ConfigureAwait(false);
-
- var searchResultArray = results.ToArray();
- results = searchResultArray;
-
- var count = searchResultArray.Length;
+ var results = GetSearchHints(query, user);
+ var totalRecordCount = results.Count;
if (query.StartIndex.HasValue)
{
- results = results.Skip(query.StartIndex.Value);
+ results = results.Skip(query.StartIndex.Value).ToList();
}
if (query.Limit.HasValue)
{
- results = results.Take(query.Limit.Value);
+ results = results.Take(query.Limit.Value).ToList();
}
return new QueryResult<SearchHintInfo>
{
- TotalRecordCount = count,
+ TotalRecordCount = totalRecordCount,
Items = results.ToArray()
};
@@ -83,24 +79,19 @@ namespace Emby.Server.Implementations.Library
/// <param name="user">The user.</param>
/// <returns>IEnumerable{SearchHintResult}.</returns>
/// <exception cref="System.ArgumentNullException">searchTerm</exception>
- private Task<IEnumerable<SearchHintInfo>> GetSearchHints(SearchQuery query, User user)
+ private List<SearchHintInfo> GetSearchHints(SearchQuery query, User user)
{
var searchTerm = query.SearchTerm;
- if (searchTerm != null)
- {
- searchTerm = searchTerm.Trim().RemoveDiacritics();
- }
-
- if (string.IsNullOrWhiteSpace(searchTerm))
+ if (string.IsNullOrEmpty(searchTerm))
{
throw new ArgumentNullException("searchTerm");
}
- var terms = GetWords(searchTerm);
+ searchTerm = searchTerm.Trim().RemoveDiacritics();
var excludeItemTypes = query.ExcludeItemTypes.ToList();
- var includeItemTypes = (query.IncludeItemTypes ?? new string[] { }).ToList();
+ var includeItemTypes = (query.IncludeItemTypes ?? Array.Empty<string>()).ToList();
excludeItemTypes.Add(typeof(Year).Name);
excludeItemTypes.Add(typeof(Folder).Name);
@@ -169,13 +160,13 @@ namespace Emby.Server.Implementations.Library
var searchQuery = new InternalItemsQuery(user)
{
- NameContains = searchTerm,
+ SearchTerm = searchTerm,
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),
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
+ IncludeItemsByName = string.IsNullOrEmpty(query.ParentId),
+ ParentId = string.IsNullOrEmpty(query.ParentId) ? Guid.Empty : new Guid(query.ParentId),
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
Recursive = true,
IsKids = query.IsKids,
@@ -201,120 +192,25 @@ namespace Emby.Server.Implementations.Library
if (searchQuery.IncludeItemTypes.Length == 1 && string.Equals(searchQuery.IncludeItemTypes[0], "MusicArtist", StringComparison.OrdinalIgnoreCase))
{
- if (searchQuery.ParentId.HasValue)
+ if (!searchQuery.ParentId.Equals(Guid.Empty))
{
- searchQuery.AncestorIds = new string[] { searchQuery.ParentId.Value.ToString("N") };
+ searchQuery.AncestorIds = new[] { searchQuery.ParentId };
}
- searchQuery.ParentId = null;
+ searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true;
- searchQuery.IncludeItemTypes = new string[] { };
- mediaItems = _libraryManager.GetArtists(searchQuery).Items.Select(i => i.Item1).ToList();
+ searchQuery.IncludeItemTypes = Array.Empty<string>();
+ mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item1).ToList();
}
else
{
mediaItems = _libraryManager.GetItemList(searchQuery);
}
- var returnValue = mediaItems.Select(item =>
- {
- var index = GetIndex(item.Name, searchTerm, terms);
-
- return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
-
- }).OrderBy(i => i.Item3).ThenBy(i => i.Item1.SortName).Select(i => new SearchHintInfo
- {
- Item = i.Item1,
- MatchedTerm = i.Item2
- });
-
- return Task.FromResult(returnValue);
- }
-
- /// <summary>
- /// Gets the index.
- /// </summary>
- /// <param name="input">The input.</param>
- /// <param name="searchInput">The search input.</param>
- /// <param name="searchWords">The search input.</param>
- /// <returns>System.Int32.</returns>
- private Tuple<string, int> GetIndex(string input, string searchInput, List<string> searchWords)
- {
- if (string.IsNullOrWhiteSpace(input))
- {
- throw new ArgumentNullException("input");
- }
-
- input = input.RemoveDiacritics();
-
- if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase))
- {
- return new Tuple<string, int>(searchInput, 0);
- }
-
- var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase);
-
- if (index == 0)
- {
- return new Tuple<string, int>(searchInput, 1);
- }
- if (index > 0)
- {
- return new Tuple<string, int>(searchInput, 2);
- }
-
- var items = GetWords(input);
-
- for (var i = 0; i < searchWords.Count; i++)
+ return mediaItems.Select(i => new SearchHintInfo
{
- var searchTerm = searchWords[i];
-
- for (var j = 0; j < items.Count; j++)
- {
- var item = items[j];
-
- if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase))
- {
- return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1));
- }
-
- index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
-
- if (index == 0)
- {
- return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1));
- }
- if (index > 0)
- {
- return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1));
- }
- }
- }
- return new Tuple<string, int>(null, -1);
- }
+ Item = i
- /// <summary>
- /// Gets the words.
- /// </summary>
- /// <param name="term">The term.</param>
- /// <returns>System.String[][].</returns>
- private List<string> GetWords(string term)
- {
- var stoplist = GetStopList().ToList();
-
- return term.Split()
- .Where(i => !string.IsNullOrWhiteSpace(i) && !stoplist.Contains(i, StringComparer.OrdinalIgnoreCase))
- .ToList();
- }
-
- private IEnumerable<string> GetStopList()
- {
- return new[]
- {
- "the",
- "a",
- "of",
- "an"
- };
+ }).ToList();
}
}
}
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 7ef5ca35e..3714a7544 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -12,7 +12,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Querying;
+using MediaBrowser.Controller.Dto;
+using System.Globalization;
namespace Emby.Server.Implementations.Library
{
@@ -29,10 +30,13 @@ namespace Emby.Server.Implementations.Library
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- public UserDataManager(ILogManager logManager, IServerConfigurationManager config)
+ private Func<IUserManager> _userManager;
+
+ public UserDataManager(ILogManager logManager, IServerConfigurationManager config, Func<IUserManager> userManager)
{
_config = config;
_logger = logManager.GetLogger(GetType().Name);
+ _userManager = userManager;
}
/// <summary>
@@ -41,7 +45,14 @@ namespace Emby.Server.Implementations.Library
/// <value>The repository.</value>
public IUserDataRepository Repository { get; set; }
- public void SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
+ public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
+ {
+ var user = _userManager().GetUserById(userId);
+
+ SaveUserData(user, item, userData, reason, cancellationToken);
+ }
+
+ public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
if (userData == null)
{
@@ -51,15 +62,13 @@ namespace Emby.Server.Implementations.Library
{
throw new ArgumentNullException("item");
}
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
cancellationToken.ThrowIfCancellationRequested();
var keys = item.GetUserDataKeys();
+ var userId = user.InternalId;
+
foreach (var key in keys)
{
Repository.SaveUserData(userId, key, userData, cancellationToken);
@@ -73,7 +82,7 @@ namespace Emby.Server.Implementations.Library
Keys = keys,
UserData = userData,
SaveReason = reason,
- UserId = userId,
+ UserId = user.Id,
Item = item
}, _logger);
@@ -88,18 +97,9 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns>
public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
{
- if (userData == null)
- {
- throw new ArgumentNullException("userData");
- }
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
+ var user = _userManager().GetUserById(userId);
- cancellationToken.ThrowIfCancellationRequested();
-
- Repository.SaveAllUserData(userId, userData, cancellationToken);
+ Repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
}
/// <summary>
@@ -109,37 +109,30 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns>
public List<UserItemData> GetAllUserData(Guid userId)
{
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
+ var user = _userManager().GetUserById(userId);
- return Repository.GetAllUserData(userId);
+ return Repository.GetAllUserData(user.InternalId);
}
public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
{
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
- if (keys == null)
- {
- throw new ArgumentNullException("keys");
- }
- if (keys.Count == 0)
- {
- throw new ArgumentException("UserData keys cannot be empty.");
- }
+ var user = _userManager().GetUserById(userId);
+
+ return GetUserData(user, itemId, keys);
+ }
+
+ public UserItemData GetUserData(User user, Guid itemId, List<string> keys)
+ {
+ var userId = user.InternalId;
var cacheKey = GetCacheKey(userId, itemId);
return _userData.GetOrAdd(cacheKey, k => GetUserDataInternal(userId, keys));
}
- private UserItemData GetUserDataInternal(Guid userId, List<string> keys)
+ private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
{
- var userData = Repository.GetUserData(userId, keys);
+ var userData = Repository.GetUserData(internalUserId, keys);
if (userData != null)
{
@@ -150,7 +143,6 @@ namespace Emby.Server.Implementations.Library
{
return new UserItemData
{
- UserId = userId,
Key = keys[0]
};
}
@@ -162,41 +154,41 @@ namespace Emby.Server.Implementations.Library
/// Gets the internal key.
/// </summary>
/// <returns>System.String.</returns>
- private string GetCacheKey(Guid userId, Guid itemId)
+ private string GetCacheKey(long internalUserId, Guid itemId)
{
- return userId.ToString("N") + itemId.ToString("N");
+ return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N");
}
- public UserItemData GetUserData(IHasUserData user, IHasUserData item)
+ public UserItemData GetUserData(User user, BaseItem item)
{
- return GetUserData(user.Id, item);
+ return GetUserData(user, item.Id, item.GetUserDataKeys());
}
- public UserItemData GetUserData(string userId, IHasUserData item)
+ public UserItemData GetUserData(string userId, BaseItem item)
{
return GetUserData(new Guid(userId), item);
}
- public UserItemData GetUserData(Guid userId, IHasUserData item)
+ public UserItemData GetUserData(Guid userId, BaseItem item)
{
return GetUserData(userId, item.Id, item.GetUserDataKeys());
}
- public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
+ public UserItemDataDto GetUserDataDto(BaseItem item, User user)
{
- var userData = GetUserData(user.Id, item);
+ var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
- item.FillUserDataDtoValues(dto, userData, null, user, new ItemFields[] { });
+ item.FillUserDataDtoValues(dto, userData, null, user, new DtoOptions());
return dto;
}
- public UserItemDataDto GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, ItemFields[] fields)
+ public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options)
{
- var userData = GetUserData(user.Id, item);
+ var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
- item.FillUserDataDtoValues(dto, userData, itemDto, user, fields);
+ item.FillUserDataDtoValues(dto, userData, itemDto, user, options);
return dto;
}
@@ -230,13 +222,15 @@ namespace Emby.Server.Implementations.Library
{
var playedToCompletion = false;
- var positionTicks = reportedPositionTicks ?? item.RunTimeTicks ?? 0;
- var hasRuntime = item.RunTimeTicks.HasValue && item.RunTimeTicks > 0;
+ var runtimeTicks = item.GetRunTimeTicksForPlayState();
+
+ var positionTicks = reportedPositionTicks ?? runtimeTicks;
+ var hasRuntime = runtimeTicks > 0;
// If a position has been reported, and if we know the duration
if (positionTicks > 0 && hasRuntime)
{
- var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
+ var pctIn = Decimal.Divide(positionTicks, runtimeTicks) * 100;
// Don't track in very beginning
if (pctIn < _config.Configuration.MinResumePct)
@@ -245,7 +239,7 @@ namespace Emby.Server.Implementations.Library
}
// If we're at the end, assume completed
- else if (pctIn > _config.Configuration.MaxResumePct || positionTicks >= item.RunTimeTicks.Value)
+ else if (pctIn > _config.Configuration.MaxResumePct || positionTicks >= runtimeTicks)
{
positionTicks = 0;
data.Played = playedToCompletion = true;
@@ -254,7 +248,7 @@ namespace Emby.Server.Implementations.Library
else
{
// Enforce MinResumeDuration
- var durationSeconds = TimeSpan.FromTicks(item.RunTimeTicks.Value).TotalSeconds;
+ var durationSeconds = TimeSpan.FromTicks(runtimeTicks).TotalSeconds;
if (durationSeconds < _config.Configuration.MinResumeDurationSeconds)
{
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 71c953b2c..b13a255aa 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -28,6 +28,11 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.Library
{
@@ -40,7 +45,9 @@ namespace Emby.Server.Implementations.Library
/// Gets the users.
/// </summary>
/// <value>The users.</value>
- public IEnumerable<User> Users { get; private set; }
+ public IEnumerable<User> Users { get { return _users; } }
+
+ private User[] _users;
/// <summary>
/// The _logger
@@ -72,6 +79,9 @@ namespace Emby.Server.Implementations.Library
private readonly IFileSystem _fileSystem;
private readonly ICryptoProvider _cryptographyProvider;
+ private IAuthenticationProvider[] _authenticationProviders;
+ private DefaultAuthenticationProvider _defaultAuthenticationProvider;
+
public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
{
_logger = logger;
@@ -86,16 +96,38 @@ namespace Emby.Server.Implementations.Library
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
ConfigurationManager = configurationManager;
- Users = new List<User>();
+ _users = Array.Empty<User>();
DeletePinFile();
}
+ public NameIdPair[] GetAuthenticationProviders()
+ {
+ return _authenticationProviders
+ .Where(i => i.IsEnabled)
+ .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
+ .ThenBy(i => i.Name)
+ .Select(i => new NameIdPair
+ {
+ Name = i.Name,
+ Id = GetAuthenticationProviderId(i)
+ })
+ .ToArray();
+ }
+
+ public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders)
+ {
+ _authenticationProviders = authenticationProviders.ToArray();
+
+ _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
+ }
+
#region UserUpdated Event
/// <summary>
/// Occurs when [user updated].
/// </summary>
public event EventHandler<GenericEventArgs<User>> UserUpdated;
+ public event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
public event EventHandler<GenericEventArgs<User>> UserLockedOut;
@@ -132,7 +164,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="System.ArgumentNullException"></exception>
public User GetUserById(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(Guid.Empty))
{
throw new ArgumentNullException("id");
}
@@ -162,7 +194,7 @@ namespace Emby.Server.Implementations.Library
public void Initialize()
{
- Users = LoadUsers();
+ _users = LoadUsers();
var users = Users.ToList();
@@ -218,7 +250,7 @@ namespace Emby.Server.Implementations.Library
return builder.ToString();
}
- public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string passwordMd5, string remoteEndPoint, bool isUserSession)
+ public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string remoteEndPoint, bool isUserSession)
{
if (string.IsNullOrWhiteSpace(username))
{
@@ -229,18 +261,16 @@ namespace Emby.Server.Implementations.Library
.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
var success = false;
+ IAuthenticationProvider authenticationProvider = null;
if (user != null)
{
- if (password != null)
- {
- hashedPassword = GetHashedString(user, password);
- }
-
// Authenticate using local credentials if not a guest
if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
{
- success = AuthenticateLocalUser(user, password, hashedPassword, remoteEndPoint);
+ var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
+ authenticationProvider = authResult.Item1;
+ success = authResult.Item2;
}
// Maybe user accidently entered connect credentials. let's be flexible
@@ -248,7 +278,7 @@ namespace Emby.Server.Implementations.Library
{
try
{
- await _connectFactory().Authenticate(user.ConnectUserName, password, passwordMd5).ConfigureAwait(false);
+ await _connectFactory().Authenticate(user.ConnectUserName, password).ConfigureAwait(false);
success = true;
}
catch
@@ -257,13 +287,43 @@ namespace Emby.Server.Implementations.Library
}
}
}
+ else
+ {
+ // user is null
+ var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
+ authenticationProvider = authResult.Item1;
+ success = authResult.Item2;
+
+ if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider))
+ {
+ user = await CreateUser(username).ConfigureAwait(false);
+
+ var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy;
+ if (hasNewUserPolicy != null)
+ {
+ var policy = hasNewUserPolicy.GetNewUserPolicy();
+ UpdateUserPolicy(user, policy, true);
+ }
+ }
+ }
+
+ if (success && user != null && authenticationProvider != null)
+ {
+ var providerId = GetAuthenticationProviderId(authenticationProvider);
+
+ if (!string.Equals(providerId, user.Policy.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
+ {
+ user.Policy.AuthenticationProviderId = providerId;
+ UpdateUserPolicy(user, user.Policy, true);
+ }
+ }
// Try originally entered username
if (!success && (user == null || !string.Equals(user.ConnectUserName, username, StringComparison.OrdinalIgnoreCase)))
{
try
{
- var connectAuthResult = await _connectFactory().Authenticate(username, password, passwordMd5).ConfigureAwait(false);
+ var connectAuthResult = await _connectFactory().Authenticate(username, password).ConfigureAwait(false);
user = Users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectAuthResult.User.Id, StringComparison.OrdinalIgnoreCase));
@@ -285,6 +345,19 @@ namespace Emby.Server.Implementations.Library
throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
}
+ if (user != null)
+ {
+ if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
+ {
+ throw new SecurityException("Forbidden.");
+ }
+
+ if (!user.IsParentalScheduleAllowed())
+ {
+ throw new SecurityException("User is not allowed access at this time.");
+ }
+ }
+
// Update LastActivityDate and LastLoginDate, then save
if (success)
{
@@ -305,34 +378,106 @@ namespace Emby.Server.Implementations.Library
return success ? user : null;
}
- private bool AuthenticateLocalUser(User user, string password, string hashedPassword, string remoteEndPoint)
+ private string GetAuthenticationProviderId(IAuthenticationProvider provider)
{
- bool success;
+ return provider.GetType().FullName;
+ }
- if (password == null)
+ private IAuthenticationProvider GetAuthenticationProvider(User user)
+ {
+ return GetAuthenticationProviders(user).First();
+ }
+
+ private IAuthenticationProvider[] GetAuthenticationProviders(User user)
+ {
+ var authenticationProviderId = user == null ? null : user.Policy.AuthenticationProviderId;
+
+ var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray();
+
+ if (!string.IsNullOrEmpty(authenticationProviderId))
{
- // legacy
- success = string.Equals(GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ providers = providers.Where(i => string.Equals(authenticationProviderId, GetAuthenticationProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
}
- else
+
+ if (providers.Length == 0)
{
- success = string.Equals(GetPasswordHash(user), GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
+ providers = new IAuthenticationProvider[] { _defaultAuthenticationProvider };
}
- if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+ return providers;
+ }
+
+ private async Task<bool> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
+ {
+ try
{
- if (password == null)
+ var requiresResolvedUser = provider as IRequiresResolvedUser;
+ if (requiresResolvedUser != null)
{
- // legacy
- success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false);
}
else
{
- success = string.Equals(GetLocalPasswordHash(user), GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
+ await provider.Authenticate(username, password).ConfigureAwait(false);
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error authenticating with provider {0}", ex, provider.Name);
+
+ return false;
+ }
+ }
+
+ private async Task<Tuple<IAuthenticationProvider, bool>> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
+ {
+ bool success = false;
+ IAuthenticationProvider authenticationProvider = null;
+
+ if (password != null && user != null)
+ {
+ // Doesn't look like this is even possible to be used, because of password == null checks below
+ hashedPassword = _defaultAuthenticationProvider.GetHashedString(user, password);
+ }
+
+ if (password == null)
+ {
+ // legacy
+ success = string.Equals(_defaultAuthenticationProvider.GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ }
+ else
+ {
+ foreach (var provider in GetAuthenticationProviders(user))
+ {
+ success = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+
+ if (success)
+ {
+ authenticationProvider = provider;
+ break;
+ }
}
}
- return success;
+ if (user != null)
+ {
+ if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+ {
+ if (password == null)
+ {
+ // legacy
+ success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ }
+ else
+ {
+ success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ }
+
+ return new Tuple<IAuthenticationProvider, bool>(authenticationProvider, success);
}
private void UpdateInvalidLoginAttemptCount(User user, int newValue)
@@ -367,63 +512,41 @@ namespace Emby.Server.Implementations.Library
}
}
- private string GetPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.Password)
- ? GetEmptyHashedString(user)
- : user.Password;
- }
-
private string GetLocalPasswordHash(User user)
{
return string.IsNullOrEmpty(user.EasyPassword)
- ? GetEmptyHashedString(user)
+ ? _defaultAuthenticationProvider.GetEmptyHashedString(user)
: user.EasyPassword;
}
private bool IsPasswordEmpty(User user, string passwordHash)
{
- return string.Equals(passwordHash, GetEmptyHashedString(user), StringComparison.OrdinalIgnoreCase);
- }
-
- private string GetEmptyHashedString(User user)
- {
- return GetHashedString(user, string.Empty);
- }
-
- /// <summary>
- /// Gets the hashed string.
- /// </summary>
- private string GetHashedString(User user, string str)
- {
- var salt = user.Salt;
- if (salt != null)
- {
- // return BCrypt.HashPassword(str, salt);
- }
-
- // legacy
- return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty);
+ return string.Equals(passwordHash, _defaultAuthenticationProvider.GetEmptyHashedString(user), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Loads the users from the repository
/// </summary>
/// <returns>IEnumerable{User}.</returns>
- private List<User> LoadUsers()
+ private User[] LoadUsers()
{
- var users = UserRepository.RetrieveAllUsers().ToList();
+ var users = UserRepository.RetrieveAllUsers();
// There always has to be at least one user.
if (users.Count == 0)
{
- var name = MakeValidUsername(Environment.UserName);
+ var defaultName = Environment.UserName;
+ if (string.IsNullOrWhiteSpace(defaultName))
+ {
+ defaultName = "MyEmbyUser";
+ }
+ var name = MakeValidUsername(defaultName);
var user = InstantiateNewUser(name);
user.DateLastSaved = DateTime.UtcNow;
- UserRepository.SaveUser(user, CancellationToken.None);
+ UserRepository.CreateUser(user);
users.Add(user);
@@ -433,7 +556,7 @@ namespace Emby.Server.Implementations.Library
UpdateUserPolicy(user, user.Policy, false);
}
- return users;
+ return users.ToArray();
}
public UserDto GetUserDto(User user, string remoteEndPoint = null)
@@ -443,9 +566,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException("user");
}
- var passwordHash = GetPasswordHash(user);
-
- var hasConfiguredPassword = !IsPasswordEmpty(user, passwordHash);
+ var hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result;
var hasConfiguredEasyPassword = !IsPasswordEmpty(user, GetLocalPasswordHash(user));
var hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
@@ -454,7 +575,7 @@ namespace Emby.Server.Implementations.Library
var dto = new UserDto
{
- Id = user.Id.ToString("N"),
+ Id = user.Id,
Name = user.Name,
HasPassword = hasPassword,
HasConfiguredPassword = hasConfiguredPassword,
@@ -577,7 +698,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException("user");
}
- if (user.Id == Guid.Empty || !Users.Any(u => u.Id.Equals(user.Id)))
+ if (user.Id.Equals(Guid.Empty) || !Users.Any(u => u.Id.Equals(user.Id)))
{
throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id));
}
@@ -585,7 +706,7 @@ namespace Emby.Server.Implementations.Library
user.DateModified = DateTime.UtcNow;
user.DateLastSaved = DateTime.UtcNow;
- UserRepository.SaveUser(user, CancellationToken.None);
+ UserRepository.UpdateUser(user);
OnUserUpdated(user);
}
@@ -626,11 +747,11 @@ namespace Emby.Server.Implementations.Library
var list = Users.ToList();
list.Add(user);
- Users = list;
+ _users = list.ToArray();
user.DateLastSaved = DateTime.UtcNow;
- UserRepository.SaveUser(user, CancellationToken.None);
+ UserRepository.CreateUser(user);
EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
@@ -658,7 +779,7 @@ namespace Emby.Server.Implementations.Library
if (user.ConnectLinkType.HasValue)
{
- await _connectFactory().RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false);
+ await _connectFactory().RemoveConnect(user).ConfigureAwait(false);
}
var allUsers = Users.ToList();
@@ -684,7 +805,7 @@ namespace Emby.Server.Implementations.Library
{
var configPath = GetConfigurationFilePath(user);
- UserRepository.DeleteUser(user, CancellationToken.None);
+ UserRepository.DeleteUser(user);
try
{
@@ -697,7 +818,7 @@ namespace Emby.Server.Implementations.Library
DeleteUserPolicy(user);
- Users = allUsers.Where(i => i.Id != user.Id).ToList();
+ _users = allUsers.Where(i => i.Id != user.Id).ToArray();
OnUserDeleted(user);
}
@@ -711,9 +832,9 @@ namespace Emby.Server.Implementations.Library
/// Resets the password by clearing it.
/// </summary>
/// <returns>Task.</returns>
- public void ResetPassword(User user)
+ public Task ResetPassword(User user)
{
- ChangePassword(user, string.Empty, null);
+ return ChangePassword(user, string.Empty);
}
public void ResetEasyPassword(User user)
@@ -721,29 +842,19 @@ namespace Emby.Server.Implementations.Library
ChangeEasyPassword(user, string.Empty, null);
}
- public void ChangePassword(User user, string newPassword, string newPasswordHash)
+ public async Task ChangePassword(User user, string newPassword)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
- if (newPassword != null)
- {
- newPasswordHash = GetHashedString(user, newPassword);
- }
-
- if (string.IsNullOrWhiteSpace(newPasswordHash))
- {
- throw new ArgumentNullException("newPasswordHash");
- }
-
if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
{
throw new ArgumentException("Passwords for guests cannot be changed.");
}
- user.Password = newPasswordHash;
+ await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
UpdateUser(user);
@@ -759,7 +870,7 @@ namespace Emby.Server.Implementations.Library
if (newPassword != null)
{
- newPasswordHash = GetHashedString(user, newPassword);
+ newPasswordHash = _defaultAuthenticationProvider.GetHashedString(user, newPassword);
}
if (string.IsNullOrWhiteSpace(newPasswordHash))
@@ -801,7 +912,7 @@ namespace Emby.Server.Implementations.Library
private PasswordPinCreationResult _lastPasswordPinCreationResult;
private int _pinAttempts;
- private PasswordPinCreationResult CreatePasswordResetPin()
+ private async Task<PasswordPinCreationResult> CreatePasswordResetPin()
{
var num = new Random().Next(1, 9999);
@@ -815,7 +926,7 @@ namespace Emby.Server.Implementations.Library
var text = new StringBuilder();
- var localAddress = _appHost.GetLocalApiUrl(CancellationToken.None).Result ?? string.Empty;
+ var localAddress = (await _appHost.GetLocalApiUrl(CancellationToken.None).ConfigureAwait(false)) ?? string.Empty;
text.AppendLine("Use your web browser to visit:");
text.AppendLine(string.Empty);
@@ -844,7 +955,7 @@ namespace Emby.Server.Implementations.Library
return result;
}
- public ForgotPasswordResult StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
+ public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
{
DeletePinFile();
@@ -872,7 +983,7 @@ namespace Emby.Server.Implementations.Library
action = ForgotPasswordAction.PinCode;
}
- var result = CreatePasswordResetPin();
+ var result = await CreatePasswordResetPin().ConfigureAwait(false);
pinFile = result.PinFile;
expirationDate = result.ExpirationDate;
}
@@ -885,7 +996,7 @@ namespace Emby.Server.Implementations.Library
};
}
- public PinRedeemResult RedeemPasswordResetPin(string pin)
+ public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
DeletePinFile();
@@ -906,7 +1017,7 @@ namespace Emby.Server.Implementations.Library
foreach (var user in users)
{
- ResetPassword(user);
+ await ResetPassword(user).ConfigureAwait(false);
if (user.Policy.IsDisabled)
{
@@ -953,7 +1064,7 @@ namespace Emby.Server.Implementations.Library
public UserPolicy GetUserPolicy(User user)
{
- var path = GetPolifyFilePath(user);
+ var path = GetPolicyFilePath(user);
try
{
@@ -988,7 +1099,7 @@ namespace Emby.Server.Implementations.Library
}
private readonly object _policySyncLock = new object();
- public void UpdateUserPolicy(string userId, UserPolicy userPolicy)
+ public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy)
{
var user = GetUserById(userId);
UpdateUserPolicy(user, userPolicy, true);
@@ -1003,7 +1114,7 @@ namespace Emby.Server.Implementations.Library
userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
}
- var path = GetPolifyFilePath(user);
+ var path = GetPolicyFilePath(user);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
@@ -1013,12 +1124,15 @@ namespace Emby.Server.Implementations.Library
user.Policy = userPolicy;
}
- UpdateConfiguration(user, user.Configuration, true);
+ if (fireEvent)
+ {
+ EventHelper.FireEventIfNotNull(UserPolicyUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
+ }
}
private void DeleteUserPolicy(User user)
{
- var path = GetPolifyFilePath(user);
+ var path = GetPolicyFilePath(user);
try
{
@@ -1037,7 +1151,7 @@ namespace Emby.Server.Implementations.Library
}
}
- private string GetPolifyFilePath(User user)
+ private string GetPolicyFilePath(User user)
{
return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
}
@@ -1075,9 +1189,14 @@ namespace Emby.Server.Implementations.Library
}
private readonly object _configSyncLock = new object();
- public void UpdateConfiguration(string userId, UserConfiguration config)
+ public void UpdateConfiguration(Guid userId, UserConfiguration config)
{
var user = GetUserById(userId);
+ UpdateConfiguration(user, config);
+ }
+
+ public void UpdateConfiguration(User user, UserConfiguration config)
+ {
UpdateConfiguration(user, config, true);
}
@@ -1106,4 +1225,56 @@ namespace Emby.Server.Implementations.Library
}
}
}
+
+ public class DeviceAccessEntryPoint : IServerEntryPoint
+ {
+ private IUserManager _userManager;
+ private IAuthenticationRepository _authRepo;
+ private IDeviceManager _deviceManager;
+ private ISessionManager _sessionManager;
+
+ public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
+ {
+ _userManager = userManager;
+ _authRepo = authRepo;
+ _deviceManager = deviceManager;
+ _sessionManager = sessionManager;
+ }
+
+ public void Run()
+ {
+ _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
+ }
+
+ private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
+ {
+ var user = e.Argument;
+ if (!user.Policy.EnableAllDevices)
+ {
+ UpdateDeviceAccess(user);
+ }
+ }
+
+ private void UpdateDeviceAccess(User user)
+ {
+ var existing = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ UserId = user.Id
+
+ }).Items;
+
+ foreach (var authInfo in existing)
+ {
+ if (!string.IsNullOrEmpty(authInfo.DeviceId) && !_deviceManager.CanAccessDevice(user, authInfo.DeviceId))
+ {
+ _sessionManager.Logout(authInfo);
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index e97bf11c3..42f922710 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -39,24 +39,15 @@ namespace Emby.Server.Implementations.Library
_config = config;
}
- public async Task<Folder[]> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
+ public Folder[] GetUserViews(UserViewQuery query)
{
var user = _userManager.GetUserById(query.UserId);
- var folders = user.RootFolder
+ var folders = _libraryManager.GetUserRootFolder()
.GetChildren(user, true)
.OfType<Folder>()
.ToList();
- if (!query.IncludeHidden)
- {
- folders = folders.Where(i =>
- {
- var hidden = i as IHiddenFromDisplay;
- return hidden == null || !hidden.IsHiddenFromUser(user);
- }).ToList();
- }
-
var groupedFolders = new List<ICollectionFolder>();
var list = new List<Folder>();
@@ -68,7 +59,7 @@ namespace Emby.Server.Implementations.Library
if (UserView.IsUserSpecific(folder))
{
- list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken));
+ list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id, folderViewType, null));
continue;
}
@@ -80,7 +71,7 @@ namespace Emby.Server.Implementations.Library
if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
- list.Add(GetUserView(folder, folderViewType, string.Empty, cancellationToken));
+ list.Add(GetUserView(folder, folderViewType, string.Empty));
}
else
{
@@ -90,7 +81,7 @@ namespace Emby.Server.Implementations.Library
foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows })
{
- var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
+ var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(i.CollectionType))
.ToList();
if (parents.Count > 0)
@@ -99,41 +90,38 @@ namespace Emby.Server.Implementations.Library
"TvShows" :
"Movies";
- list.Add(GetUserView(parents, viewType, localizationKey, string.Empty, user, query.PresetViews, cancellationToken));
+ list.Add(GetUserView(parents, viewType, localizationKey, string.Empty, user, query.PresetViews));
}
}
if (_config.Configuration.EnableFolderView)
{
var name = _localizationManager.GetLocalizedString("Folders");
- list.Add(_libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken));
+ list.Add(_libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty));
}
if (query.IncludeExternalContent)
{
- var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
+ var channelResult = _channelManager.GetChannelsInternal(new ChannelQuery
{
UserId = query.UserId
-
- }, cancellationToken).ConfigureAwait(false);
+ });
var channels = channelResult.Items;
- if (_config.Configuration.EnableChannelView && channels.Length > 0)
- {
- list.Add(_channelManager.GetInternalChannelFolder(cancellationToken));
- }
- else
- {
- list.AddRange(channels);
- }
+ list.AddRange(channels);
- if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
+ if (_liveTvManager.GetEnabledUsers().Select(i => i.Id).Contains(query.UserId))
{
list.Add(_liveTvManager.GetInternalLiveTvFolder(CancellationToken.None));
}
}
+ if (!query.IncludeHidden)
+ {
+ list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N"))).ToList();
+ }
+
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
var orders = user.Configuration.OrderedViews.ToList();
@@ -148,7 +136,7 @@ namespace Emby.Server.Implementations.Library
var view = i as UserView;
if (view != null)
{
- if (view.DisplayParentId != Guid.Empty)
+ if (!view.DisplayParentId.Equals(Guid.Empty))
{
index = orders.IndexOf(view.DisplayParentId.ToString("N"));
}
@@ -162,21 +150,21 @@ namespace Emby.Server.Implementations.Library
.ToArray();
}
- public UserView GetUserSubViewWithName(string name, string parentId, string type, string sortName, CancellationToken cancellationToken)
+ public UserView GetUserSubViewWithName(string name, Guid parentId, string type, string sortName)
{
var uniqueId = parentId + "subview" + type;
- return _libraryManager.GetNamedView(name, parentId, type, sortName, uniqueId, cancellationToken);
+ return _libraryManager.GetNamedView(name, parentId, type, sortName, uniqueId);
}
- public UserView GetUserSubView(string parentId, string type, string localizationKey, string sortName, CancellationToken cancellationToken)
+ public UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName)
{
var name = _localizationManager.GetLocalizedString(localizationKey);
- return GetUserSubViewWithName(name, parentId, type, sortName, cancellationToken);
+ return GetUserSubViewWithName(name, parentId, type, sortName);
}
- private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews, CancellationToken cancellationToken)
+ private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews)
{
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
@@ -185,16 +173,16 @@ namespace Emby.Server.Implementations.Library
return (Folder)parents[0];
}
- return GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken);
+ return GetUserView((Folder)parents[0], viewType, string.Empty);
}
var name = _localizationManager.GetLocalizedString(localizationKey);
- return _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken);
+ return _libraryManager.GetNamedView(user, name, viewType, sortName);
}
- public UserView GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken)
+ public UserView GetUserView(Folder parent, string viewType, string sortName)
{
- return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
+ return _libraryManager.GetShadowView(parent, viewType, sortName);
}
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options)
@@ -246,9 +234,26 @@ namespace Emby.Server.Implementations.Library
var parents = new List<BaseItem>();
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (!parentId.Equals(Guid.Empty))
{
- var parent = _libraryManager.GetItemById(parentId) as Folder;
+ var parentItem = _libraryManager.GetItemById(parentId);
+ var parentItemChannel = parentItem as Channel;
+ if (parentItemChannel != null)
+ {
+ return _channelManager.GetLatestChannelItemsInternal(new InternalItemsQuery(user)
+ {
+ ChannelIds = new [] { parentId },
+ IsPlayed = request.IsPlayed,
+ StartIndex = request.StartIndex,
+ Limit = request.Limit,
+ IncludeItemTypes = request.IncludeItemTypes,
+ EnableTotalRecordCount = false
+
+
+ }, CancellationToken.None).Result.Items.ToList();
+ }
+
+ var parent = parentItem as Folder;
if (parent != null)
{
parents.Add(parent);
@@ -264,7 +269,7 @@ namespace Emby.Server.Implementations.Library
if (parents.Count == 0)
{
- parents = user.RootFolder.GetChildren(user, true)
+ parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
.ToList();
@@ -275,6 +280,24 @@ namespace Emby.Server.Implementations.Library
return new List<BaseItem>();
}
+ if (includeItemTypes.Length == 0)
+ {
+ // Handle situations with the grouping setting, e.g. movies showing up in tv, etc.
+ // Thanks to mixed content libraries included in the UserView
+ var hasCollectionType = parents.OfType<UserView>().ToArray();
+ if (hasCollectionType.Length > 0)
+ {
+ if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)))
+ {
+ includeItemTypes = new string[] { "Movie" };
+ }
+ else if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)))
+ {
+ includeItemTypes = new string[] { "Episode" };
+ }
+ }
+ }
+
var mediaTypes = new List<string>();
if (includeItemTypes.Length == 0)
@@ -285,6 +308,7 @@ namespace Emby.Server.Implementations.Library
{
case CollectionType.Books:
mediaTypes.Add(MediaType.Book);
+ mediaTypes.Add(MediaType.Audio);
break;
case CollectionType.Games:
mediaTypes.Add(MediaType.Game);
@@ -318,12 +342,12 @@ namespace Emby.Server.Implementations.Library
typeof(MusicGenre).Name,
typeof(Genre).Name
- } : new string[] { };
+ } : Array.Empty<string>();
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = includeItemTypes,
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
ExcludeItemTypes = excludeItemTypes,
IsVirtualItem = false,
diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
index 1a53ad672..cd2aab4c8 100644
--- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
@@ -81,33 +81,27 @@ namespace Emby.Server.Implementations.Library.Validators
progress.Report(percent);
}
- names = names.Select(i => i.RemoveDiacritics()).DistinctNames().ToList();
-
- var artistEntities = _libraryManager.GetItemList(new InternalItemsQuery
+ var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(MusicArtist).Name }
-
+ IncludeItemTypes = new[] { typeof(MusicArtist).Name },
+ IsDeadArtist = true,
+ IsLocked = false
}).Cast<MusicArtist>().ToList();
- foreach (var artist in artistEntities)
+ foreach (var item in deadEntities)
{
- if (!artist.IsAccessedByName)
+ if (!item.IsAccessedByName)
{
continue;
}
+
+ _logger.Info("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
- var name = (artist.Name ?? string.Empty).RemoveDiacritics();
-
- if (!names.Contains(name, StringComparer.OrdinalIgnoreCase))
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
- _logger.Info("Deleting dead artist {0} {1}.", artist.Id.ToString("N"), artist.Name);
+ DeleteFileLocation = false
- await _libraryManager.DeleteItem(artist, new DeleteOptions
- {
- DeleteFileLocation = false
-
- }).ConfigureAwait(false);
- }
+ }, false);
}
progress.Report(100);
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 39630cf96..1f4e1de92 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -1,18 +1,11 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Validators
@@ -73,7 +66,7 @@ namespace Emby.Server.Implementations.Library.Validators
var options = new MetadataRefreshOptions(_fileSystem)
{
- ImageRefreshMode = ImageRefreshMode.ValidationOnly,
+ ImageRefreshMode = MetadataRefreshMode.ValidationOnly,
MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
};
@@ -96,6 +89,23 @@ namespace Emby.Server.Implementations.Library.Validators
progress.Report(100 * percent);
}
+ var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Person).Name },
+ IsDeadPerson = true,
+ IsLocked = false
+ });
+
+ foreach (var item in deadEntities)
+ {
+ _logger.Info("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
+
+ _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+ }, false);
+ }
+
progress.Report(100);
_logger.Info("People validation complete");
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 97b8ff0ac..f306309b3 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -68,6 +68,24 @@ namespace Emby.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Studio).Name },
+ IsDeadStudio = true,
+ IsLocked = false
+ });
+
+ foreach (var item in deadEntities)
+ {
+ _logger.Info("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
+
+ _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }, false);
+ }
+
progress.Report(100);
}
}
diff --git a/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs b/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
deleted file mode 100644
index 0c8049d8b..000000000
--- a/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Emby.Server.Implementations.LiveTv
-{
- public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
- {
- private readonly ILiveTvManager _liveTvManager;
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
- private readonly IApplicationHost _appHost;
-
- public ChannelImageProvider(ILiveTvManager liveTvManager, IHttpClient httpClient, ILogger logger, IApplicationHost appHost)
- {
- _liveTvManager = liveTvManager;
- _httpClient = httpClient;
- _logger = logger;
- _appHost = appHost;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
- {
- return new[] { ImageType.Primary };
- }
-
- public async Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
- {
- var liveTvItem = (LiveTvChannel)item;
-
- var imageResponse = new DynamicImageResponse();
-
- var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
-
- if (service != null && !item.HasImage(ImageType.Primary))
- {
- try
- {
- var response = await service.GetChannelImageAsync(liveTvItem.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (response != null)
- {
- imageResponse.HasImage = true;
- imageResponse.Stream = response.Stream;
- imageResponse.Format = response.Format;
- }
- }
- catch (NotImplementedException)
- {
- }
- }
-
- return imageResponse;
- }
-
- public string Name
- {
- get { return "Live TV Service Provider"; }
- }
-
- public bool Supports(IHasMetadata item)
- {
- return item is LiveTvChannel;
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- return GetSupportedImages(item).Any(i => !item.HasImage(i));
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index f0578d9ef..0c7980ca0 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -17,12 +17,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
+ private readonly IStreamHelper _streamHelper;
- public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem)
+ public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem, IStreamHelper streamHelper)
{
_logger = logger;
_httpClient = httpClient;
_fileSystem = fileSystem;
+ _streamHelper = streamHelper;
}
public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
@@ -50,7 +52,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.Info("Copying recording stream to file {0}", targetFile);
- // The media source if infinite so we need to handle stopping ourselves
+ // The media source is infinite so we need to handle stopping ourselves
var durationToken = new CancellationTokenSource(duration);
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
@@ -90,45 +92,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var durationToken = new CancellationTokenSource(duration);
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
- await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
+ await _streamHelper.CopyUntilCancelled(response.Content, output, 81920, cancellationToken).ConfigureAwait(false);
}
}
_logger.Info("Recording completed to file {0}", targetFile);
}
-
- private const int BufferSize = 81920;
- public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
- {
- byte[] buffer = new byte[BufferSize];
-
- while (!cancellationToken.IsCancellationRequested)
- {
- var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false);
-
- //var position = fs.Position;
- //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
-
- if (bytesRead == 0)
- {
- await Task.Delay(100).ConfigureAwait(false);
- }
- }
- }
-
- private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken)
- {
- int bytesRead;
- int totalBytesRead = 0;
-
- while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
- {
- destination.Write(buffer, 0, bytesRead);
-
- totalBytesRead += bytesRead;
- }
-
- return totalBytesRead;
- }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 35d2d3c0a..d21abb74e 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -39,6 +39,10 @@ using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Reflection;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.MediaInfo;
+using Emby.Server.Implementations.Library;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
@@ -62,16 +66,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IMediaEncoder _mediaEncoder;
private readonly IProcessFactory _processFactory;
private readonly ISystemEvents _systemEvents;
+ private readonly IAssemblyInfo _assemblyInfo;
+ private IMediaSourceManager _mediaSourceManager;
public static EmbyTV Current;
public event EventHandler DataSourceChanged;
- public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged;
+ public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
+ public event EventHandler<GenericEventArgs<string>> TimerCancelled;
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, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents)
+ private readonly IStreamHelper _streamHelper;
+
+ public EmbyTV(IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, IAssemblyInfo assemblyInfo, ILogger logger, IJsonSerializer jsonSerializer, IPowerManagement powerManagement, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents)
{
Current = this;
@@ -88,9 +97,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_systemEvents = systemEvents;
_liveTvManager = (LiveTvManager)liveTvManager;
_jsonSerializer = jsonSerializer;
+ _assemblyInfo = assemblyInfo;
+ _mediaSourceManager = mediaSourceManager;
+ _streamHelper = streamHelper;
_seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
- _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory);
+ _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory, powerManagement);
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -104,12 +116,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public void Start()
+ public async void Start()
{
_timerProvider.RestartTimers();
_systemEvents.Resume += _systemEvents_Resume;
- CreateRecordingFolders();
+ await CreateRecordingFolders().ConfigureAwait(false);
}
private void _systemEvents_Resume(object sender, EventArgs e)
@@ -117,83 +129,78 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_timerProvider.RestartTimers();
}
- private void OnRecordingFoldersChanged()
+ private async void OnRecordingFoldersChanged()
{
- CreateRecordingFolders();
+ await CreateRecordingFolders().ConfigureAwait(false);
}
- internal void CreateRecordingFolders()
+ internal async Task CreateRecordingFolders()
{
try
{
- CreateRecordingFoldersInternal();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error creating recording folders", ex);
- }
- }
+ var recordingFolders = GetRecordingFolders();
- internal void CreateRecordingFoldersInternal()
- {
- var recordingFolders = GetRecordingFolders();
+ var virtualFolders = _libraryManager.GetVirtualFolders()
+ .ToList();
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
+ var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
- var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
+ var pathsAdded = new List<string>();
- var pathsAdded = new List<string>();
+ foreach (var recordingFolder in recordingFolders)
+ {
+ var pathsToCreate = recordingFolder.Locations
+ .Where(i => !allExistingPaths.Any(p => _fileSystem.AreEqual(p, i)))
+ .ToList();
- foreach (var recordingFolder in recordingFolders)
- {
- var pathsToCreate = recordingFolder.Locations
- .Where(i => !allExistingPaths.Any(p => _fileSystem.AreEqual(p, i)))
- .ToList();
+ if (pathsToCreate.Count == 0)
+ {
+ continue;
+ }
- if (pathsToCreate.Count == 0)
- {
- continue;
- }
+ var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
- var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
+ var libraryOptions = new LibraryOptions
+ {
+ PathInfos = mediaPathInfos
+ };
+ try
+ {
+ await _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, libraryOptions, true).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error creating virtual folder", ex);
+ }
- var libraryOptions = new LibraryOptions
- {
- PathInfos = mediaPathInfos
- };
- try
- {
- _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, libraryOptions, true);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error creating virtual folder", ex);
+ pathsAdded.AddRange(pathsToCreate);
}
- pathsAdded.AddRange(pathsToCreate);
- }
+ var config = GetConfiguration();
- var config = GetConfiguration();
+ var pathsToRemove = config.MediaLocationsCreated
+ .Except(recordingFolders.SelectMany(i => i.Locations))
+ .ToList();
- var pathsToRemove = config.MediaLocationsCreated
- .Except(recordingFolders.SelectMany(i => i.Locations))
- .ToList();
+ if (pathsAdded.Count > 0 || pathsToRemove.Count > 0)
+ {
+ pathsAdded.InsertRange(0, config.MediaLocationsCreated);
+ config.MediaLocationsCreated = pathsAdded.Except(pathsToRemove).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
+ _config.SaveConfiguration("livetv", config);
+ }
- if (pathsAdded.Count > 0 || pathsToRemove.Count > 0)
- {
- pathsAdded.InsertRange(0, config.MediaLocationsCreated);
- config.MediaLocationsCreated = pathsAdded.Except(pathsToRemove).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
- _config.SaveConfiguration("livetv", config);
+ foreach (var path in pathsToRemove)
+ {
+ await RemovePathFromLibrary(path).ConfigureAwait(false);
+ }
}
-
- foreach (var path in pathsToRemove)
+ catch (Exception ex)
{
- RemovePathFromLibrary(path);
+ _logger.ErrorException("Error creating recording folders", ex);
}
}
- private void RemovePathFromLibrary(string path)
+ private async Task RemovePathFromLibrary(string path)
{
_logger.Debug("Removing path from library: {0}", path);
@@ -213,7 +220,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
// remove entire virtual folder
try
{
- _libraryManager.RemoveVirtualFolder(virtualFolder.Name, true);
+ await _libraryManager.RemoveVirtualFolder(virtualFolder.Name, true).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -272,33 +279,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public string HomePageUrl
{
- get { return "http://emby.media"; }
- }
-
- public async Task<LiveTvServiceStatusInfo> GetStatusInfoAsync(CancellationToken cancellationToken)
- {
- var status = new LiveTvServiceStatusInfo();
- var list = new List<LiveTvTunerInfo>();
-
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- var tuners = await hostInstance.GetTunerInfos(cancellationToken).ConfigureAwait(false);
-
- list.AddRange(tuners);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting tuners", ex);
- }
- }
-
- status.Tuners = list;
- status.Status = LiveTvServiceStatus.Ok;
- status.Version = _appHost.ApplicationVersion.ToString();
- status.IsVisible = false;
- return status;
+ get { return "https://emby.media"; }
}
public async Task RefreshSeriesTimers(CancellationToken cancellationToken, IProgress<double> progress)
@@ -315,7 +296,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var timers = await GetTimersAsync(cancellationToken).ConfigureAwait(false);
- var tempChannelCache = new Dictionary<string, LiveTvChannel>();
+ var tempChannelCache = new Dictionary<Guid, LiveTvChannel>();
foreach (var timer in timers)
{
@@ -408,33 +389,90 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl))
{
tunerChannel.ImageUrl = epgChannel.ImageUrl;
- tunerChannel.HasImage = true;
}
}
}
}
- private readonly ConcurrentDictionary<string, List<ChannelInfo>> _epgChannels =
- new ConcurrentDictionary<string, List<ChannelInfo>>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, EpgChannelData> _epgChannels =
+ new ConcurrentDictionary<string, EpgChannelData>(StringComparer.OrdinalIgnoreCase);
- private async Task<List<ChannelInfo>> GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken)
+ private async Task<EpgChannelData> GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken)
{
- List<ChannelInfo> result;
+ EpgChannelData result;
if (!enableCache || !_epgChannels.TryGetValue(info.Id, out result))
{
- result = await provider.GetChannels(info, cancellationToken).ConfigureAwait(false);
+ var channels = await provider.GetChannels(info, cancellationToken).ConfigureAwait(false);
- foreach (var channel in result)
+ foreach (var channel in channels)
{
_logger.Info("Found epg channel in {0} {1} {2} {3}", provider.Name, info.ListingsId, channel.Name, channel.Id);
}
+ result = new EpgChannelData(channels);
_epgChannels.AddOrUpdate(info.Id, result, (k, v) => result);
}
return result;
}
+ private class EpgChannelData
+ {
+ public EpgChannelData(List<ChannelInfo> channels)
+ {
+ ChannelsById = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+ ChannelsByNumber = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+ ChannelsByName = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var channel in channels)
+ {
+ ChannelsById[channel.Id] = channel;
+
+ if (!string.IsNullOrEmpty(channel.Number))
+ {
+ ChannelsByNumber[channel.Number] = channel;
+ }
+
+ var normalizedName = NormalizeName(channel.Name ?? string.Empty);
+ if (!string.IsNullOrWhiteSpace(normalizedName))
+ {
+ ChannelsByName[normalizedName] = channel;
+ }
+ }
+ }
+
+ private Dictionary<string, ChannelInfo> ChannelsById { get; set; }
+ private Dictionary<string, ChannelInfo> ChannelsByNumber { get; set; }
+ private Dictionary<string, ChannelInfo> ChannelsByName { get; set; }
+
+ public ChannelInfo GetChannelById(string id)
+ {
+ ChannelInfo result = null;
+
+ ChannelsById.TryGetValue(id, out result);
+
+ return result;
+ }
+
+ public ChannelInfo GetChannelByNumber(string number)
+ {
+ ChannelInfo result = null;
+
+ ChannelsByNumber.TryGetValue(number, out result);
+
+ return result;
+ }
+
+ public ChannelInfo GetChannelByName(string name)
+ {
+ ChannelInfo result = null;
+
+ ChannelsByName.TryGetValue(name, out result);
+
+ return result;
+ }
+ }
+
private async Task<ChannelInfo> GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken)
{
var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false);
@@ -454,12 +492,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return channelId;
}
- private ChannelInfo GetEpgChannelFromTunerChannel(ListingsProviderInfo info, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
+ internal ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
+ {
+ return GetEpgChannelFromTunerChannel(mappings, tunerChannel, new EpgChannelData(epgChannels));
+ }
+
+ private ChannelInfo GetEpgChannelFromTunerChannel(ListingsProviderInfo info, ChannelInfo tunerChannel, EpgChannelData epgChannels)
{
return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels);
}
- public ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
+ private ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, EpgChannelData epgChannelData)
{
if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
{
@@ -470,7 +513,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
mappedTunerChannelId = tunerChannel.Id;
}
- var channel = epgChannels.FirstOrDefault(i => string.Equals(mappedTunerChannelId, i.Id, StringComparison.OrdinalIgnoreCase));
+ var channel = epgChannelData.GetChannelById(mappedTunerChannelId);
if (channel != null)
{
@@ -493,7 +536,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
mappedTunerChannelId = tunerChannelId;
}
- var channel = epgChannels.FirstOrDefault(i => string.Equals(mappedTunerChannelId, i.Id, StringComparison.OrdinalIgnoreCase));
+ var channel = epgChannelData.GetChannelById(mappedTunerChannelId);
if (channel != null)
{
@@ -510,7 +553,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
tunerChannelNumber = tunerChannel.Number;
}
- var channel = epgChannels.FirstOrDefault(i => string.Equals(tunerChannelNumber, i.Number, StringComparison.OrdinalIgnoreCase));
+ var channel = epgChannelData.GetChannelByNumber(tunerChannelNumber);
if (channel != null)
{
@@ -522,7 +565,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var normalizedName = NormalizeName(tunerChannel.Name);
- var channel = epgChannels.FirstOrDefault(i => string.Equals(normalizedName, NormalizeName(i.Name ?? string.Empty), StringComparison.OrdinalIgnoreCase));
+ var channel = epgChannelData.GetChannelByName(normalizedName);
if (channel != null)
{
@@ -533,7 +576,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return null;
}
- private string NormalizeName(string value)
+ private static string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
}
@@ -575,7 +618,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var timer in timers)
{
- CancelTimerInternal(timer.Id, true);
+ CancelTimerInternal(timer.Id, true, true);
}
var remove = _seriesTimerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
@@ -583,16 +626,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
_seriesTimerProvider.Delete(remove);
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
- private void CancelTimerInternal(string timerId, bool isSeriesCancelled)
+ private void CancelTimerInternal(string timerId, bool isSeriesCancelled, bool isManualCancellation)
{
var timer = _timerProvider.GetTimer(timerId);
if (timer != null)
{
+ var statusChanging = timer.Status != RecordingStatus.Cancelled;
timer.Status = RecordingStatus.Cancelled;
+ if (isManualCancellation)
+ {
+ timer.IsManual = true;
+ }
+
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
{
_timerProvider.Delete(timer);
@@ -601,6 +650,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
_timerProvider.AddOrUpdate(timer, false);
}
+
+ if (statusChanging && TimerCancelled != null)
+ {
+ TimerCancelled(this, new GenericEventArgs<string>(timerId));
+ }
}
ActiveRecordingInfo activeRecordingInfo;
@@ -613,13 +667,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public Task CancelTimerAsync(string timerId, CancellationToken cancellationToken)
{
- CancelTimerInternal(timerId, false);
- return Task.FromResult(true);
+ CancelTimerInternal(timerId, false, true);
+ return Task.CompletedTask;
}
public Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken)
{
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
@@ -675,6 +729,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
timer.IsManual = true;
_timerProvider.Add(timer);
+
+ if (TimerCreated != null)
+ {
+ TimerCreated(this, new GenericEventArgs<TimerInfo>(timer));
+ }
+
return Task.FromResult(timer.Id);
}
@@ -776,7 +836,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_timerProvider.Update(existingTimer);
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
private void UpdateExistingTimerWithNewMetadata(TimerInfo existingTimer, TimerInfo updatedTimer)
@@ -788,16 +848,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
existingTimer.EpisodeNumber = updatedTimer.EpisodeNumber;
existingTimer.EpisodeTitle = updatedTimer.EpisodeTitle;
existingTimer.Genres = updatedTimer.Genres;
- existingTimer.HomePageUrl = updatedTimer.HomePageUrl;
- existingTimer.IsKids = updatedTimer.IsKids;
- existingTimer.IsNews = updatedTimer.IsNews;
existingTimer.IsMovie = updatedTimer.IsMovie;
existingTimer.IsSeries = updatedTimer.IsSeries;
- existingTimer.IsLive = updatedTimer.IsLive;
- existingTimer.IsPremiere = updatedTimer.IsPremiere;
+ existingTimer.Tags = updatedTimer.Tags;
existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
existingTimer.IsRepeat = updatedTimer.IsRepeat;
- existingTimer.IsSports = updatedTimer.IsSports;
existingTimer.Name = updatedTimer.Name;
existingTimer.OfficialRating = updatedTimer.OfficialRating;
existingTimer.OriginalAirDate = updatedTimer.OriginalAirDate;
@@ -807,26 +862,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
existingTimer.SeasonNumber = updatedTimer.SeasonNumber;
existingTimer.StartDate = updatedTimer.StartDate;
existingTimer.ShowId = updatedTimer.ShowId;
- }
-
- public Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
- {
- return new List<RecordingInfo>();
+ existingTimer.ProviderIds = updatedTimer.ProviderIds;
+ existingTimer.SeriesProviderIds = updatedTimer.SeriesProviderIds;
}
public string GetActiveRecordingPath(string id)
@@ -909,6 +946,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
defaults.SeriesId = program.SeriesId;
defaults.ProgramId = program.Id;
defaults.RecordNewOnly = !program.IsRepeat;
+ defaults.Name = program.Name;
}
defaults.SkipEpisodesInLibrary = defaults.RecordNewOnly;
@@ -972,10 +1010,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
program.ChannelId = channelId;
- if (provider.Item2.EnableNewProgramIds)
- {
- program.Id += "_" + channelId;
- }
+ program.Id += "_" + channelId;
}
if (programs.Count > 0)
@@ -1000,26 +1035,51 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
.ToList();
}
- public Task<MediaSourceInfo> GetRecordingStream(string recordingId, string streamId, CancellationToken cancellationToken)
+ public Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
- private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1);
- private readonly List<ILiveStream> _liveStreams = new List<ILiveStream>();
-
- public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+ public async Task<ILiveStream> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- var result = await GetChannelStreamWithDirectStreamProvider(channelId, streamId, cancellationToken).ConfigureAwait(false);
+ _logger.Info("Streaming Channel " + channelId);
- return result.Item1;
- }
+ var result = string.IsNullOrEmpty(streamId) ?
+ null :
+ currentLiveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken)
- {
- var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false);
+ if (result != null && result.EnableStreamSharing)
+ {
+ result.ConsumerCount++;
+
+ _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
+
+ return result;
+ }
+
+ foreach (var hostInstance in _liveTvManager.TunerHosts)
+ {
+ try
+ {
+ result = await hostInstance.GetChannelStream(channelId, streamId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
+
+ var openedMediaSource = result.MediaSource;
+
+ result.OriginalStreamId = streamId;
+
+ _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}", streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
+
+ return result;
+ }
+ catch (FileNotFoundException)
+ {
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ }
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(result.Item2, result.Item1 as IDirectStreamProvider);
+ throw new Exception("Tuner not found.");
}
private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, bool enableStreamSharing)
@@ -1039,93 +1099,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return mediaSource;
}
- public async Task<ILiveStream> GetLiveStream(string uniqueId)
- {
- await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- return _liveStreams
- .FirstOrDefault(i => string.Equals(i.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase));
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
- }
-
- public async Task<List<ILiveStream>> GetLiveStreams(TunerHostInfo host, CancellationToken cancellationToken)
- {
- //await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- //try
- //{
- var hostId = host.Id;
-
- return _liveStreams
- .Where(i => string.Equals(i.TunerHostId, hostId, StringComparison.OrdinalIgnoreCase))
- .ToList();
- //}
- //finally
- //{
- // _liveStreamsSemaphore.Release();
- //}
- }
-
- private async Task<Tuple<ILiveStream, MediaSourceInfo>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
- {
- _logger.Info("Streaming Channel " + channelId);
-
- await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
-
- if (result != null && result.EnableStreamSharing)
- {
- var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
- result.SharedStreamIds.Add(openedMediaSource.Id);
-
- _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
-
- return new Tuple<ILiveStream, MediaSourceInfo>(result, openedMediaSource);
- }
-
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
-
- var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
-
- result.SharedStreamIds.Add(openedMediaSource.Id);
- _liveStreams.Add(result);
-
- result.OriginalStreamId = streamId;
-
- _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
- streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
-
- return new Tuple<ILiveStream, MediaSourceInfo>(result, openedMediaSource);
- }
- catch (FileNotFoundException)
- {
- }
- catch (OperationCanceledException)
- {
- }
- }
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
-
- throw new Exception("Tuner not found.");
- }
-
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(channelId))
@@ -1153,47 +1126,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new NotImplementedException();
}
- public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
- {
- ActiveRecordingInfo info;
-
- recordingId = recordingId.Replace("recording", string.Empty);
-
- if (_activeRecordings.TryGetValue(recordingId, out info))
- {
- var stream = new MediaSourceInfo
- {
- Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
- Id = recordingId,
- SupportsDirectPlay = false,
- SupportsDirectStream = true,
- SupportsTranscoding = true,
- IsInfiniteStream = true,
- RequiresOpening = false,
- RequiresClosing = false,
- Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
- BufferMs = 0,
- IgnoreDts = true,
- IgnoreIndex = true
- };
-
- var isAudio = false;
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
-
- return new List<MediaSourceInfo>
- {
- stream
- };
- }
-
- throw new FileNotFoundException();
- }
-
public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken)
{
var stream = new MediaSourceInfo
{
- Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+ EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+ EncoderProtocol = MediaProtocol.Http,
+ Path = info.Path,
+ Protocol = MediaProtocol.File,
Id = info.Id,
SupportsDirectPlay = false,
SupportsDirectStream = true,
@@ -1201,14 +1141,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IsInfiniteStream = true,
RequiresOpening = false,
RequiresClosing = false,
- Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
BufferMs = 0,
IgnoreDts = true,
IgnoreIndex = true
};
var isAudio = false;
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
+ await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths).AddMediaInfoWithProbe(stream, isAudio, false, cancellationToken).ConfigureAwait(false);
return new List<MediaSourceInfo>
{
@@ -1216,48 +1155,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
};
}
- public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
+ public Task CloseLiveStream(string id, CancellationToken cancellationToken)
{
- // Ignore the consumer id
- //id = id.Substring(id.IndexOf('_') + 1);
-
- await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var stream = _liveStreams.FirstOrDefault(i => i.SharedStreamIds.Contains(id));
- if (stream != null)
- {
- stream.SharedStreamIds.Remove(id);
-
- _logger.Info("Live stream {0} consumer count is now {1}", id, stream.ConsumerCount);
-
- if (stream.ConsumerCount <= 0)
- {
- _liveStreams.Remove(stream);
-
- _logger.Info("Closing live stream {0}", id);
-
- stream.Close();
- _logger.Info("Live stream {0} closed successfully", id);
- }
- }
- else
- {
- _logger.Warn("Live stream not found: {0}, unable to close", id);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream", ex);
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
+ return Task.CompletedTask;
}
public Task RecordLiveStream(string id, CancellationToken cancellationToken)
@@ -1274,7 +1174,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var timer = e.Argument;
- _logger.Info("Recording timer fired.");
+ _logger.Info("Recording timer fired for {0}.", timer.Name);
try
{
@@ -1321,7 +1221,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private string GetRecordingPath(TimerInfo timer, out string seriesPath)
+ private string GetRecordingPath(TimerInfo timer, RemoteSearchResult metadata, out string seriesPath)
{
var recordPath = RecordingPath;
var config = GetConfiguration();
@@ -1342,7 +1242,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
recordPath = Path.Combine(recordPath, "Series");
}
- var folderName = _fileSystem.GetValidFilename(timer.Name).Trim();
+ // trim trailing period from the folder name
+ var folderName = _fileSystem.GetValidFilename(timer.Name).Trim().TrimEnd('.').Trim();
+
+ if (metadata != null && metadata.ProductionYear.HasValue)
+ {
+ folderName += " (" + metadata.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
+ }
// Can't use the year here in the folder name because it is the year of the episode, not the series.
recordPath = Path.Combine(recordPath, folderName);
@@ -1375,6 +1281,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
folderName += " (" + timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
}
+
+ // trim trailing period from the folder name
+ folderName = folderName.TrimEnd('.').Trim();
+
recordPath = Path.Combine(recordPath, folderName);
}
else if (timer.IsKids)
@@ -1389,6 +1299,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
folderName += " (" + timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
}
+
+ // trim trailing period from the folder name
+ folderName = folderName.TrimEnd('.').Trim();
+
recordPath = Path.Combine(recordPath, folderName);
}
else if (timer.IsSports)
@@ -1438,23 +1352,36 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
string seriesPath = null;
- var recordPath = GetRecordingPath(timer, out seriesPath);
+ var remoteMetadata = await FetchInternetMetadata(timer, CancellationToken.None).ConfigureAwait(false);
+ var recordPath = GetRecordingPath(timer, remoteMetadata, out seriesPath);
var recordingStatus = RecordingStatus.New;
- var recorder = await GetRecorder().ConfigureAwait(false);
-
string liveStreamId = null;
+ var channelItem = _liveTvManager.GetLiveTvChannel(timer, this);
+
try
{
- var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
+ var allMediaSources = await _mediaSourceManager.GetPlayackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false);
+
+ var mediaStreamInfo = allMediaSources[0];
+ IDirectStreamProvider directStreamProvider = null;
- _logger.Info("Opening recording stream from tuner provider");
- var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
- .ConfigureAwait(false);
+ if (mediaStreamInfo.RequiresOpening)
+ {
+ var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(new LiveStreamRequest
+ {
+ ItemId = channelItem.Id,
+ OpenToken = mediaStreamInfo.OpenToken
- var mediaStreamInfo = liveStreamInfo.Item2;
- liveStreamId = mediaStreamInfo.Id;
+ }, CancellationToken.None).ConfigureAwait(false);
+
+ mediaStreamInfo = liveStreamResponse.Item1.MediaSource;
+ liveStreamId = mediaStreamInfo.LiveStreamId;
+ directStreamProvider = liveStreamResponse.Item2;
+ }
+
+ var recorder = GetRecorder(mediaStreamInfo);
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id);
@@ -1478,13 +1405,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
await SaveRecordingMetadata(timer, recordPath, seriesPath).ConfigureAwait(false);
- CreateRecordingFolders();
+ await CreateRecordingFolders().ConfigureAwait(false);
TriggerRefresh(recordPath);
EnforceKeepUpTo(timer, seriesPath);
};
- await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
+ await recorder.Record(directStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
recordingStatus = RecordingStatus.Completed;
_logger.Info("Recording completed: {0}", recordPath);
@@ -1504,7 +1431,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- await CloseLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
+ await _mediaSourceManager.CloseLiveStream(liveStreamId).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1544,6 +1471,34 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
+ private async Task<RemoteSearchResult> FetchInternetMetadata(TimerInfo timer, CancellationToken cancellationToken)
+ {
+ if (timer.IsSeries)
+ {
+ if (timer.SeriesProviderIds.Count == 0)
+ {
+ return null;
+ }
+
+ var query = new RemoteSearchQuery<SeriesInfo>()
+ {
+ SearchInfo = new SeriesInfo
+ {
+ ProviderIds = timer.SeriesProviderIds,
+ Name = timer.Name,
+ MetadataCountryCode = _config.Configuration.MetadataCountryCode,
+ MetadataLanguage = _config.Configuration.PreferredMetadataLanguage
+ }
+ };
+
+ var results = await _providerManager.GetRemoteSearchResults<Series, SeriesInfo>(query, cancellationToken).ConfigureAwait(false);
+
+ return results.FirstOrDefault();
+ }
+
+ return null;
+ }
+
private void DeleteFileIfEmpty(string path)
{
var file = _fileSystem.GetFileInfo(path);
@@ -1573,8 +1528,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
{
- ValidateChildren = true,
- RefreshPaths = new List<string>
+ RefreshPaths = new string[]
{
path,
_fileSystem.GetDirectoryName(path),
@@ -1627,7 +1581,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var seriesTimerId = timer.SeriesTimerId;
var seriesTimer = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, seriesTimerId, StringComparison.OrdinalIgnoreCase));
- if (seriesTimer == null || seriesTimer.KeepUpTo <= 1)
+ if (seriesTimer == null || seriesTimer.KeepUpTo <= 0)
{
return;
}
@@ -1654,7 +1608,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
.Skip(seriesTimer.KeepUpTo - 1)
.ToList();
- await DeleteLibraryItemsForTimers(timersToDelete).ConfigureAwait(false);
+ DeleteLibraryItemsForTimers(timersToDelete);
var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
@@ -1665,14 +1619,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery
{
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
IsVirtualItem = false,
IsFolder = false,
Recursive = true,
DtoOptions = new DtoOptions(true)
}))
- .Where(i => i.LocationType == LocationType.FileSystem && _fileSystem.FileExists(i.Path))
+ .Where(i => i.IsFileProtocol && _fileSystem.FileExists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1)
.ToList();
@@ -1680,11 +1634,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- await _libraryManager.DeleteItem(item, new DeleteOptions
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = true
- }).ConfigureAwait(false);
+ }, true);
}
catch (Exception ex)
{
@@ -1699,7 +1653,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
- private async Task DeleteLibraryItemsForTimers(List<TimerInfo> timers)
+ private void DeleteLibraryItemsForTimers(List<TimerInfo> timers)
{
foreach (var timer in timers)
{
@@ -1710,7 +1664,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- await DeleteLibraryItemForTimer(timer).ConfigureAwait(false);
+ DeleteLibraryItemForTimer(timer);
}
catch (Exception ex)
{
@@ -1719,17 +1673,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private async Task DeleteLibraryItemForTimer(TimerInfo timer)
+ private void DeleteLibraryItemForTimer(TimerInfo timer)
{
var libraryItem = _libraryManager.FindByPath(timer.RecordingPath, false);
if (libraryItem != null)
{
- await _libraryManager.DeleteItem(libraryItem, new DeleteOptions
+ _libraryManager.DeleteItem(libraryItem, new DeleteOptions
{
DeleteFileLocation = true
- }).ConfigureAwait(false);
+ }, true);
}
else
{
@@ -1783,57 +1737,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false;
}
- private async Task<IRecorder> GetRecorder()
+ private IRecorder GetRecorder(MediaSourceInfo mediaSource)
{
- var config = GetConfiguration();
-
- var regInfo = await _liveTvManager.GetRegistrationInfo("dvr").ConfigureAwait(false);
-
- if (regInfo.IsValid)
+ if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- if (config.EnableRecordingEncoding)
- {
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient, _processFactory, _config);
- }
-
- return new DirectRecorder(_logger, _httpClient, _fileSystem);
-
- //var options = new LiveTvOptions
- //{
- // EnableOriginalAudioWithEncodedRecordings = true,
- // RecordedVideoCodec = "copy",
- // RecordingEncodingFormat = "ts"
- //};
- //return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, options, _httpClient, _processFactory, _config);
+ return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _httpClient, _processFactory, _config, _assemblyInfo);
}
- throw new InvalidOperationException("Emby DVR Requires an active Emby Premiere subscription.");
+ return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper);
}
private void OnSuccessfulRecording(TimerInfo timer, string path)
{
- //if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize)
- //{
- // try
- // {
- // // this is to account for the library monitor holding a lock for additional time after the change is complete.
- // // ideally this shouldn't be hard-coded
- // await Task.Delay(30000).ConfigureAwait(false);
-
- // var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
-
- // var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false);
-
- // if (result.Status == FileSortingStatus.Success)
- // {
- // return;
- // }
- // }
- // catch (Exception ex)
- // {
- // _logger.ErrorException("Error processing new recording", ex);
- // }
- //}
PostProcessRecording(timer, path);
}
@@ -2026,7 +1941,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
program = new LiveTvProgram
{
Name = timer.Name,
- HomePageUrl = timer.HomePageUrl,
Overview = timer.Overview,
Genres = timer.Genres,
CommunityRating = timer.CommunityRating,
@@ -2040,16 +1954,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (timer.IsSports)
{
- AddGenre(program.Genres, "Sports");
+ program.AddGenre("Sports");
}
if (timer.IsKids)
{
- AddGenre(program.Genres, "Kids");
- AddGenre(program.Genres, "Children");
+ program.AddGenre("Kids");
+ program.AddGenre("Children");
}
if (timer.IsNews)
{
- AddGenre(program.Genres, "News");
+ program.AddGenre("News");
}
if (timer.IsProgramSeries)
@@ -2097,12 +2011,30 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteStartDocument(true);
writer.WriteStartElement("tvshow");
+ string id;
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id))
+ {
+ writer.WriteElementString("id", id);
+ }
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id))
+ {
+ writer.WriteElementString("imdb_id", id);
+ }
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id))
+ {
+ writer.WriteElementString("tmdbid", id);
+ }
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
+ {
+ writer.WriteElementString("zap2itid", id);
+ }
+
if (!string.IsNullOrWhiteSpace(timer.Name))
{
writer.WriteElementString("title", timer.Name);
}
- if (!string.IsNullOrEmpty(timer.OfficialRating))
+ if (!string.IsNullOrWhiteSpace(timer.OfficialRating))
{
writer.WriteElementString("mpaa", timer.OfficialRating);
}
@@ -2139,11 +2071,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var options = _config.GetNfoConfiguration();
+ var isSeriesEpisode = timer.IsProgramSeries;
+
using (XmlWriter writer = XmlWriter.Create(stream, settings))
{
writer.WriteStartDocument(true);
- if (timer.IsProgramSeries)
+ if (isSeriesEpisode)
{
writer.WriteStartElement("episodedetails");
@@ -2152,11 +2086,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("title", timer.EpisodeTitle);
}
- if (item.PremiereDate.HasValue)
+ var premiereDate = item.PremiereDate ?? (!timer.IsRepeat ? DateTime.UtcNow : (DateTime?)null);
+
+ if (premiereDate.HasValue)
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString("aired", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString("aired", premiereDate.Value.ToLocalTime().ToString(formatString));
}
if (item.IndexNumber.HasValue)
@@ -2210,11 +2146,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("plot", overview);
- if (lockData)
- {
- writer.WriteElementString("lockdata", true.ToString().ToLower());
- }
-
if (item.CommunityRating.HasValue)
{
writer.WriteElementString("rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
@@ -2225,12 +2156,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("genre", genre);
}
- if (!string.IsNullOrWhiteSpace(item.HomePageUrl))
- {
- writer.WriteElementString("website", item.HomePageUrl);
- }
-
- var people = item.Id == Guid.Empty ? new List<PersonInfo>() : _libraryManager.GetPeople(item);
+ var people = item.Id.Equals(Guid.Empty) ? new List<PersonInfo>() : _libraryManager.GetPeople(item);
var directors = people
.Where(i => IsPersonType(i, PersonType.Director))
@@ -2268,26 +2194,38 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var imdb = item.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrEmpty(imdb))
{
- if (item is Series)
- {
- writer.WriteElementString("imdb_id", imdb);
- }
- else
+ if (!isSeriesEpisode)
{
- writer.WriteElementString("imdbid", imdb);
+ writer.WriteElementString("id", imdb);
}
+
+ writer.WriteElementString("imdbid", imdb);
+
+ // No need to lock if we have identified the content already
+ lockData = false;
}
var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
if (!string.IsNullOrEmpty(tvdb))
{
writer.WriteElementString("tvdbid", tvdb);
+
+ // No need to lock if we have identified the content already
+ lockData = false;
}
var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
if (!string.IsNullOrEmpty(tmdb))
{
writer.WriteElementString("tmdbid", tmdb);
+
+ // No need to lock if we have identified the content already
+ lockData = false;
+ }
+
+ if (lockData)
+ {
+ writer.WriteElementString("lockdata", true.ToString().ToLower());
}
if (item.CriticRating.HasValue)
@@ -2328,7 +2266,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var query = new InternalItemsQuery
{
- ItemIds = new[] { _liveTvManager.GetInternalProgramId(Name, programId).ToString("N") },
+ ItemIds = new[] { _liveTvManager.GetInternalProgramId(programId) },
Limit = 1,
DtoOptions = new DtoOptions()
};
@@ -2358,12 +2296,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
},
MinStartDate = startDateUtc.AddMinutes(-3),
MaxStartDate = startDateUtc.AddMinutes(3),
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) }
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) }
};
if (!string.IsNullOrWhiteSpace(channelId))
{
- query.ChannelIds = new[] { _liveTvManager.GetInternalChannelId(Name, channelId).ToString("N") };
+ query.ChannelIds = new[] { _liveTvManager.GetInternalChannelId(Name, channelId) };
}
return _libraryManager.GetItemList(query).Cast<LiveTvProgram>().FirstOrDefault();
@@ -2383,7 +2321,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!seriesTimer.RecordAnyTime)
{
- if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(5).Ticks)
+ if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks)
{
return true;
}
@@ -2473,6 +2411,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
enabledTimersForSeries.Add(timer);
}
_timerProvider.Add(timer);
+
+ if (TimerCreated != null)
+ {
+ TimerCreated(this, new GenericEventArgs<TimerInfo>(timer));
+ }
}
else
{
@@ -2525,7 +2468,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
.Select(i => i.Id)
.ToList();
- var deleteStatuses = new List<RecordingStatus>
+ var deleteStatuses = new[]
{
RecordingStatus.New
};
@@ -2538,7 +2481,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var timer in deletes)
{
- CancelTimerInternal(timer.Id, false);
+ CancelTimerInternal(timer.Id, false, false);
}
}
}
@@ -2550,11 +2493,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new ArgumentNullException("seriesTimer");
}
- if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId))
- {
- return new List<TimerInfo>();
- }
-
var query = new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
@@ -2566,21 +2504,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
MinEndDate = DateTime.UtcNow
};
+ if (string.IsNullOrEmpty(seriesTimer.SeriesId))
+ {
+ query.Name = seriesTimer.Name;
+ }
+
if (!seriesTimer.RecordAnyChannel)
{
- query.ChannelIds = new[] { _liveTvManager.GetInternalChannelId(Name, seriesTimer.ChannelId).ToString("N") };
+ query.ChannelIds = new[] { _liveTvManager.GetInternalChannelId(Name, seriesTimer.ChannelId) };
}
- var tempChannelCache = new Dictionary<string, LiveTvChannel>();
+ var tempChannelCache = new Dictionary<Guid, LiveTvChannel>();
return _libraryManager.GetItemList(query).Cast<LiveTvProgram>().Select(i => CreateTimer(i, seriesTimer, tempChannelCache));
}
- private TimerInfo CreateTimer(LiveTvProgram parent, SeriesTimerInfo seriesTimer, Dictionary<string, LiveTvChannel> tempChannelCache)
+ private TimerInfo CreateTimer(LiveTvProgram parent, SeriesTimerInfo seriesTimer, Dictionary<Guid, LiveTvChannel> tempChannelCache)
{
string channelId = seriesTimer.RecordAnyChannel ? null : seriesTimer.ChannelId;
- if (string.IsNullOrWhiteSpace(channelId) && !string.IsNullOrWhiteSpace(parent.ChannelId))
+ if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.Equals(Guid.Empty))
{
LiveTvChannel channel;
@@ -2633,15 +2576,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void CopyProgramInfoToTimerInfo(LiveTvProgram programInfo, TimerInfo timerInfo)
{
- var tempChannelCache = new Dictionary<string, LiveTvChannel>();
+ var tempChannelCache = new Dictionary<Guid, LiveTvChannel>();
CopyProgramInfoToTimerInfo(programInfo, timerInfo, tempChannelCache);
}
- private void CopyProgramInfoToTimerInfo(LiveTvProgram programInfo, TimerInfo timerInfo, Dictionary<string, LiveTvChannel> tempChannelCache)
+ private void CopyProgramInfoToTimerInfo(LiveTvProgram programInfo, TimerInfo timerInfo, Dictionary<Guid, LiveTvChannel> tempChannelCache)
{
string channelId = null;
- if (!string.IsNullOrWhiteSpace(programInfo.ChannelId))
+ if (!programInfo.ChannelId.Equals(Guid.Empty))
{
LiveTvChannel channel;
@@ -2679,24 +2622,33 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
timerInfo.SeasonNumber = programInfo.ParentIndexNumber;
timerInfo.EpisodeNumber = programInfo.IndexNumber;
timerInfo.IsMovie = programInfo.IsMovie;
- timerInfo.IsKids = programInfo.IsKids;
- timerInfo.IsNews = programInfo.IsNews;
- timerInfo.IsSports = programInfo.IsSports;
timerInfo.ProductionYear = programInfo.ProductionYear;
timerInfo.EpisodeTitle = programInfo.EpisodeTitle;
timerInfo.OriginalAirDate = programInfo.PremiereDate;
timerInfo.IsProgramSeries = programInfo.IsSeries;
timerInfo.IsSeries = programInfo.IsSeries;
- timerInfo.IsLive = programInfo.IsLive;
- timerInfo.IsPremiere = programInfo.IsPremiere;
- timerInfo.HomePageUrl = programInfo.HomePageUrl;
timerInfo.CommunityRating = programInfo.CommunityRating;
timerInfo.Overview = programInfo.Overview;
timerInfo.OfficialRating = programInfo.OfficialRating;
timerInfo.IsRepeat = programInfo.IsRepeat;
timerInfo.SeriesId = programInfo.ExternalSeriesId;
+ timerInfo.ProviderIds = programInfo.ProviderIds;
+ timerInfo.Tags = programInfo.Tags;
+
+ var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var providerId in timerInfo.ProviderIds)
+ {
+ var srch = "Series";
+ if (providerId.Key.StartsWith(srch, StringComparison.OrdinalIgnoreCase))
+ {
+ seriesProviderIds[providerId.Key.Substring(srch.Length)] = providerId.Value;
+ }
+ }
+
+ timerInfo.SeriesProviderIds = seriesProviderIds;
}
private bool IsProgramAlreadyInLibrary(TimerInfo program)
@@ -2708,7 +2660,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IncludeItemTypes = new[] { typeof(Series).Name },
Name = program.Name
- }).Select(i => i.ToString("N")).ToArray();
+ }).ToArray();
if (seriesIds.Length == 0)
{
@@ -2745,7 +2697,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
pair.Value.CancellationTokenSource.Cancel();
}
- GC.SuppressFinalize(this);
}
public List<VirtualFolderInfo> GetRecordingFolders()
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
deleted file mode 100644
index b339537ae..000000000
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Security;
-
-namespace Emby.Server.Implementations.LiveTv.EmbyTV
-{
- public class EmbyTVRegistration : IRequiresRegistration
- {
- private readonly ISecurityManager _securityManager;
-
- public static EmbyTVRegistration Instance;
-
- public EmbyTVRegistration(ISecurityManager securityManager)
- {
- _securityManager = securityManager;
- Instance = this;
- }
-
- private bool? _isXmlTvEnabled;
-
- public Task LoadRegistrationInfoAsync()
- {
- _isXmlTvEnabled = null;
- return Task.FromResult(true);
- }
-
- public async Task<bool> EnableXmlTv()
- {
- if (!_isXmlTvEnabled.HasValue)
- {
- var info = await _securityManager.GetRegistrationStatus("xmltv").ConfigureAwait(false);
- _isXmlTvEnabled = info.IsValid;
- }
- return _isXmlTvEnabled.Value;
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index d6f5e0d9f..9506a82be 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Reflection;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
@@ -32,7 +33,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IHttpClient _httpClient;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
- private readonly LiveTvOptions _liveTvOptions;
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
@@ -41,37 +41,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly IServerConfigurationManager _config;
+ private readonly IAssemblyInfo _assemblyInfo;
- public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config)
+ public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config, IAssemblyInfo assemblyInfo)
{
_logger = logger;
_fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
- _liveTvOptions = liveTvOptions;
_httpClient = httpClient;
_processFactory = processFactory;
_config = config;
- }
-
- private string OutputFormat
- {
- get
- {
- var format = _liveTvOptions.RecordingEncodingFormat;
-
- if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase))
- {
- return "mkv";
- }
- if (string.Equals(format, "ts", StringComparison.OrdinalIgnoreCase))
- {
- return "ts";
- }
-
- return "mkv";
- }
+ _assemblyInfo = assemblyInfo;
}
private bool CopySubtitles
@@ -85,20 +67,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
{
- var extension = OutputFormat;
-
- if (string.Equals(extension, "mpegts", StringComparison.OrdinalIgnoreCase))
- {
- extension = "ts";
- }
-
- return Path.ChangeExtension(targetFile, "." + extension);
+ return Path.ChangeExtension(targetFile, ".ts");
}
public async Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
- //var durationToken = new CancellationTokenSource(duration);
- //cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+ // The media source is infinite so we need to handle stopping ourselves
+ var durationToken = new CancellationTokenSource(duration);
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
@@ -185,6 +161,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
videoArgs += " -fflags +genpts";
var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
+ durationParam = string.Empty;
var flags = new List<string>();
if (mediaSource.IgnoreDts)
@@ -208,9 +185,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
var videoStream = mediaSource.VideoStream;
- var videoDecoder = videoStream == null ? null : new EncodingHelper(_mediaEncoder, _fileSystem, null).GetVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions());
+ string videoDecoder = null;
- if (!string.IsNullOrWhiteSpace(videoDecoder))
+ if (!string.IsNullOrEmpty(videoDecoder))
{
inputModifier += " " + videoDecoder;
}
@@ -222,7 +199,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (mediaSource.RequiresLooping)
{
- inputModifier += " -stream_loop -1";
+ inputModifier += " -stream_loop -1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2";
}
var analyzeDurationSeconds = 5;
@@ -255,39 +232,27 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty;
- // do not copy aac because many players have difficulty with aac_latm
- if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings && !string.Equals(inputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
- {
- return "-codec:a:0 copy";
- }
+ return "-codec:a:0 copy";
- var audioChannels = 2;
- var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audioStream != null)
- {
- audioChannels = audioStream.Channels ?? audioChannels;
- }
- return "-codec:a:0 aac -strict experimental -ab 320000";
+ //var audioChannels = 2;
+ //var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+ //if (audioStream != null)
+ //{
+ // audioChannels = audioStream.Channels ?? audioChannels;
+ //}
+ //return "-codec:a:0 aac -strict experimental -ab 320000";
}
private bool EncodeVideo(MediaSourceInfo mediaSource)
{
- var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
- return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced);
+ return false;
}
protected string GetOutputSizeParam()
{
var filters = new List<string>();
-
- if (string.Equals(GetEncodingOptions().DeinterlaceMethod, "bobandweave", StringComparison.OrdinalIgnoreCase))
- {
- filters.Add("yadif=1:-1:0");
- }
- else
- {
- filters.Add("yadif=0:-1:0");
- }
+
+ filters.Add("yadif=0:-1:0");
var output = string.Empty;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 7c5f630a7..cc9e80a82 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -12,7 +12,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public void Dispose()
{
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index a5712b480..e694a8281 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -26,11 +26,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
else if (info.OriginalAirDate.HasValue)
{
- name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd");
+ if (info.OriginalAirDate.Value.Date.Equals(info.StartDate.Date))
+ {
+ name += " " + GetDateString(info.StartDate);
+ }
+ else
+ {
+ name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd");
+ }
}
else
{
- name += " " + DateTime.Now.ToString("yyyy-MM-dd");
+ name += " " + GetDateString(info.StartDate);
}
if (!string.IsNullOrWhiteSpace(info.EpisodeTitle))
@@ -50,10 +57,24 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
else
{
- name += " " + info.StartDate.ToString("yyyy-MM-dd");
+ name += " " + GetDateString(info.StartDate);
}
return name;
}
+
+ private static string GetDateString(DateTime date)
+ {
+ date = date.ToLocalTime();
+
+ return string.Format("{0}_{1}_{2}_{3}_{4}_{5}",
+ date.Year.ToString("0000", CultureInfo.InvariantCulture),
+ date.Month.ToString("00", CultureInfo.InvariantCulture),
+ date.Day.ToString("00", CultureInfo.InvariantCulture),
+ date.Hour.ToString("00", CultureInfo.InvariantCulture),
+ date.Minute.ToString("00", CultureInfo.InvariantCulture),
+ date.Second.ToString("00", CultureInfo.InvariantCulture)
+ );
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index 843ba7e42..63cd26c7e 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public override void Add(SeriesTimerInfo item)
{
- if (string.IsNullOrWhiteSpace(item.Id))
+ if (string.IsNullOrEmpty(item.Id))
{
throw new ArgumentException("SeriesTimerInfo.Id cannot be null or empty.");
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 380b24800..b5f93b882 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
@@ -23,12 +24,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
private readonly ITimerFactory _timerFactory;
+ private readonly IPowerManagement _powerManagement;
- public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory)
+ public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory, IPowerManagement powerManagement)
: base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
_logger = logger1;
_timerFactory = timerFactory;
+ _powerManagement = powerManagement;
}
public void RestartTimers()
@@ -37,7 +40,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var item in GetAll().ToList())
{
- AddOrUpdateSystemTimer(item);
+ AddOrUpdateSystemTimer(item, false);
}
}
@@ -60,7 +63,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public override void Update(TimerInfo item)
{
base.Update(item);
- AddOrUpdateSystemTimer(item);
+ AddOrUpdateSystemTimer(item, false);
}
public void AddOrUpdate(TimerInfo item, bool resetTimer)
@@ -85,13 +88,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public override void Add(TimerInfo item)
{
- if (string.IsNullOrWhiteSpace(item.Id))
+ if (string.IsNullOrEmpty(item.Id))
{
throw new ArgumentException("TimerInfo.Id cannot be null or empty.");
}
base.Add(item);
- AddOrUpdateSystemTimer(item);
+ AddOrUpdateSystemTimer(item, true);
}
private bool ShouldStartTimer(TimerInfo item)
@@ -105,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return true;
}
- private void AddOrUpdateSystemTimer(TimerInfo item)
+ private void AddOrUpdateSystemTimer(TimerInfo item, bool scheduleSystemWakeTimer)
{
StopTimer(item);
@@ -125,6 +128,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var dueTime = startDate - now;
StartTimer(item, dueTime);
+
+ if (scheduleSystemWakeTimer && dueTime >= TimeSpan.FromMinutes(15))
+ {
+ ScheduleSystemWakeTimer(startDate, item.Name);
+ }
+ }
+
+ private void ScheduleSystemWakeTimer(DateTime startDate, string displayName)
+ {
+ try
+ {
+ _powerManagement.ScheduleWake(startDate.AddMinutes(-5), displayName);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error scheduling wake timer", ex);
+ }
}
private void StartTimer(TimerInfo item, TimeSpan dueTime)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index e210e2224..9021666a3 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -16,6 +16,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.LiveTv.Listings
{
@@ -63,7 +66,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(channelId))
+ if (string.IsNullOrEmpty(channelId))
{
throw new ArgumentNullException("channelId");
}
@@ -75,7 +78,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
_logger.Warn("SchedulesDirect token is empty, returning empty program list");
return programsInfo;
@@ -246,8 +249,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
DateTime endAt = startAt.AddSeconds(programInfo.duration);
ProgramAudio audioType = ProgramAudio.Stereo;
- bool repeat = programInfo.@new == null;
-
var programId = programInfo.programID ?? string.Empty;
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
@@ -293,14 +294,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CommunityRating = null,
EpisodeTitle = episodeTitle,
Audio = audioType,
- IsRepeat = repeat,
+ //IsNew = programInfo.@new ?? false,
+ IsRepeat = programInfo.@new == null,
IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
ImageUrl = details.primaryImage,
ThumbImageUrl = details.thumbImage,
IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase),
IsMovie = IsMovie(details),
- Etag = programInfo.md5
+ Etag = programInfo.md5,
+ IsLive = string.Equals(programInfo.liveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
+ IsPremiere = programInfo.premiere || (programInfo.isPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
};
var showId = programId;
@@ -356,6 +360,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
info.SeriesId = programId.Substring(0, 10);
+ info.SeriesProviderIds[MetadataProviders.Zap2It.ToString()] = info.SeriesId;
+
if (details.metadata != null)
{
foreach (var metadataProgram in details.metadata)
@@ -376,12 +382,21 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- if (!string.IsNullOrWhiteSpace(details.originalAirDate) && (!info.IsSeries || info.IsRepeat))
+ if (!string.IsNullOrWhiteSpace(details.originalAirDate))
{
info.OriginalAirDate = DateTime.Parse(details.originalAirDate);
info.ProductionYear = info.OriginalAirDate.Value.Year;
}
+ if (details.movie != null)
+ {
+ int year;
+ if (!string.IsNullOrEmpty(details.movie.year) && int.TryParse(details.movie.year, out year))
+ {
+ info.ProductionYear = year;
+ }
+ }
+
if (details.genres != null)
{
info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
@@ -506,8 +521,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
{
- return _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.ShowImages>>(
- innerResponse2.Content);
+ return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
+ innerResponse2.Content).ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -545,7 +560,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using (Stream responce = httpResponse.Content)
{
- var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Headends>>(responce).ConfigureAwait(false);
if (root != null)
{
@@ -589,7 +604,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
var password = info.Password;
- if (string.IsNullOrWhiteSpace(password))
+ if (string.IsNullOrEmpty(password))
{
return null;
}
@@ -607,7 +622,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_tokens.TryAdd(username, savedToken);
}
- if (!string.IsNullOrWhiteSpace(savedToken.Name) && !string.IsNullOrWhiteSpace(savedToken.Value))
+ if (!string.IsNullOrEmpty(savedToken.Name) && !string.IsNullOrEmpty(savedToken.Value))
{
long ticks;
if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
@@ -740,7 +755,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using (var responce = await Post(httpOptions, false, null).ConfigureAwait(false))
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Token>(responce.Content);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Token>(responce.Content).ConfigureAwait(false);
if (root.message == "OK")
{
_logger.Info("Authenticated with Schedules Direct token: " + root.token);
@@ -755,12 +770,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var token = await GetToken(info, cancellationToken);
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
throw new ArgumentException("Authentication required.");
}
- if (string.IsNullOrWhiteSpace(info.ListingsId))
+ if (string.IsNullOrEmpty(info.ListingsId))
{
throw new ArgumentException("Listings Id required");
}
@@ -796,14 +811,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(info.ListingsId))
+ if (string.IsNullOrEmpty(info.ListingsId))
{
throw new ArgumentException("Listings Id required");
}
var token = await GetToken(info, cancellationToken);
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
throw new Exception("token required");
}
@@ -826,7 +841,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using (var response = httpResponse.Content)
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Lineups>(response).ConfigureAwait(false);
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
}
@@ -848,18 +863,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
if (validateLogin)
{
- if (string.IsNullOrWhiteSpace(info.Username))
+ if (string.IsNullOrEmpty(info.Username))
{
throw new ArgumentException("Username is required");
}
- if (string.IsNullOrWhiteSpace(info.Password))
+ if (string.IsNullOrEmpty(info.Password))
{
throw new ArgumentException("Password is required");
}
}
if (validateListings)
{
- if (string.IsNullOrWhiteSpace(info.ListingsId))
+ if (string.IsNullOrEmpty(info.ListingsId))
{
throw new ArgumentException("Listings Id required");
}
@@ -881,14 +896,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken)
{
var listingsId = info.ListingsId;
- if (string.IsNullOrWhiteSpace(listingsId))
+ if (string.IsNullOrEmpty(listingsId))
{
throw new Exception("ListingsId required");
}
var token = await GetToken(info, cancellationToken);
- if (string.IsNullOrWhiteSpace(token))
+ if (string.IsNullOrEmpty(token))
{
throw new Exception("token required");
}
@@ -911,7 +926,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using (var response = httpResponse.Content)
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
+ var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Channel>(response).ConfigureAwait(false);
_logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
_logger.Info("Mapping Stations to Channel");
@@ -951,7 +966,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (station.logo != null)
{
channelInfo.ImageUrl = station.logo.URL;
- channelInfo.HasImage = true;
}
}
@@ -1112,6 +1126,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Rating> ratings { get; set; }
public bool? @new { get; set; }
public Multipart multipart { get; set; }
+ public string liveTapeDelay { get; set; }
+ public bool premiere { get; set; }
+ public bool repeat { get; set; }
+ public string isPremiereOrFinale { get; set; }
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
deleted file mode 100644
index 7c251e303..000000000
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ /dev/null
@@ -1,315 +0,0 @@
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.XmlTv.Classes;
-using Emby.XmlTv.Entities;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-
-namespace Emby.Server.Implementations.LiveTv.Listings
-{
- public class XmlTvListingsProvider : IListingsProvider
- {
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IZipClient _zipClient;
-
- public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem, IZipClient zipClient)
- {
- _config = config;
- _httpClient = httpClient;
- _logger = logger;
- _fileSystem = fileSystem;
- _zipClient = zipClient;
- }
-
- public string Name
- {
- get { return "XmlTV"; }
- }
-
- public string Type
- {
- get { return "xmltv"; }
- }
-
- private string GetLanguage(ListingsProviderInfo info)
- {
- if (!string.IsNullOrWhiteSpace(info.PreferredLanguage))
- {
- return info.PreferredLanguage;
- }
-
- return _config.Configuration.PreferredMetadataLanguage;
- }
-
- private async Task<string> GetXml(string path, CancellationToken cancellationToken)
- {
- _logger.Info("xmltv path: {0}", path);
-
- if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return UnzipIfNeeded(path, path);
- }
-
- var cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + ".xml";
- var cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
- if (_fileSystem.FileExists(cacheFile))
- {
- return UnzipIfNeeded(path, cacheFile);
- }
-
- _logger.Info("Downloading xmltv listings from {0}", path);
-
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = path,
- Progress = new SimpleProgress<Double>(),
- DecompressionMethod = CompressionMethod.Gzip,
-
- // It's going to come back gzipped regardless of this value
- // So we need to make sure the decompression method is set to gzip
- EnableHttpCompression = true,
-
- UserAgent = "Emby/3.0"
-
- }).ConfigureAwait(false);
-
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFile));
-
- _fileSystem.CopyFile(tempFile, cacheFile, true);
-
- return UnzipIfNeeded(path, cacheFile);
- }
-
- private string UnzipIfNeeded(string originalUrl, string file)
- {
- var ext = Path.GetExtension(originalUrl.Split('?')[0]);
-
- if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- var tempFolder = ExtractGz(file);
- return FindXmlFile(tempFolder);
- }
- catch (Exception ex)
- {
- //_logger.ErrorException("Error extracting from gz file {0}", ex, file);
- }
-
- try
- {
- var tempFolder = ExtractFirstFileFromGz(file);
- return FindXmlFile(tempFolder);
- }
- catch (Exception ex)
- {
- //_logger.ErrorException("Error extracting from zip file {0}", ex, file);
- }
- }
-
- return file;
- }
-
- private string ExtractFirstFileFromGz(string file)
- {
- using (var stream = _fileSystem.OpenRead(file))
- {
- var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
- _fileSystem.CreateDirectory(tempFolder);
-
- _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
-
- return tempFolder;
- }
- }
-
- private string ExtractGz(string file)
- {
- using (var stream = _fileSystem.OpenRead(file))
- {
- var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
- _fileSystem.CreateDirectory(tempFolder);
-
- _zipClient.ExtractAllFromGz(stream, tempFolder, true);
-
- return tempFolder;
- }
- }
-
- private string FindXmlFile(string directory)
- {
- return _fileSystem.GetFiles(directory, true)
- .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
- .Select(i => i.FullName)
- .FirstOrDefault();
- }
-
- public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
- if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false))
- {
- var length = endDateUtc - startDateUtc;
- if (length.TotalDays > 1)
- {
- endDateUtc = startDateUtc.AddDays(1);
- }
- }
-
- _logger.Debug("Getting xmltv programs for channel {0}", channelId);
-
- var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- _logger.Debug("Opening XmlTvReader for {0}", path);
- var reader = new XmlTvReader(path, GetLanguage(info));
-
- var results = reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken);
- return results.Select(p => GetProgramInfo(p, info));
- }
-
- private ProgramInfo GetProgramInfo(XmlTvProgram p, ListingsProviderInfo info)
- {
- var episodeTitle = p.Episode == null ? null : p.Episode.Title;
-
- var programInfo = new ProgramInfo
- {
- ChannelId = p.ChannelId,
- EndDate = GetDate(p.EndDate),
- EpisodeNumber = p.Episode == null ? null : p.Episode.Episode,
- EpisodeTitle = episodeTitle,
- Genres = p.Categories,
- StartDate = GetDate(p.StartDate),
- Name = p.Title,
- Overview = p.Description,
- ProductionYear = !p.CopyrightDate.HasValue ? (int?)null : p.CopyrightDate.Value.Year,
- SeasonNumber = p.Episode == null ? null : p.Episode.Series,
- IsSeries = p.Episode != null,
- IsRepeat = p.IsPreviouslyShown && !p.IsNew,
- IsPremiere = p.Premiere != null,
- IsKids = p.Categories.Any(c => info.KidsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)),
- IsMovie = p.Categories.Any(c => info.MovieCategories.Contains(c, StringComparer.OrdinalIgnoreCase)),
- IsNews = p.Categories.Any(c => info.NewsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)),
- IsSports = p.Categories.Any(c => info.SportsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)),
- ImageUrl = p.Icon != null && !String.IsNullOrEmpty(p.Icon.Source) ? p.Icon.Source : null,
- HasImage = p.Icon != null && !String.IsNullOrEmpty(p.Icon.Source),
- OfficialRating = p.Rating != null && !String.IsNullOrEmpty(p.Rating.Value) ? p.Rating.Value : null,
- CommunityRating = p.StarRating.HasValue ? p.StarRating.Value : (float?)null,
- SeriesId = p.Episode != null ? p.Title.GetMD5().ToString("N") : null
- };
-
- if (!string.IsNullOrWhiteSpace(p.ProgramId))
- {
- programInfo.ShowId = p.ProgramId;
- }
- else
- {
- var uniqueString = (p.Title ?? string.Empty) + (episodeTitle ?? string.Empty) + (p.IceTvEpisodeNumber ?? string.Empty);
-
- if (programInfo.SeasonNumber.HasValue)
- {
- uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture);
- }
- if (programInfo.EpisodeNumber.HasValue)
- {
- uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- programInfo.ShowId = uniqueString.GetMD5().ToString("N");
-
- // If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped
- if (programInfo.IsSeries && !programInfo.IsRepeat)
- {
- if ((programInfo.EpisodeNumber ?? 0) == 0)
- {
- programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
- }
- }
- }
-
- // Construct an id from the channel and start date
- programInfo.Id = String.Format("{0}_{1:O}", p.ChannelId, p.StartDate);
-
- if (programInfo.IsMovie)
- {
- programInfo.IsSeries = false;
- programInfo.EpisodeNumber = null;
- programInfo.EpisodeTitle = null;
- }
-
- return programInfo;
- }
-
- private DateTime GetDate(DateTime date)
- {
- if (date.Kind != DateTimeKind.Utc)
- {
- date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
- }
- return date;
- }
-
- public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
- {
- // Assume all urls are valid. check files for existence
- if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase) && !_fileSystem.FileExists(info.Path))
- {
- throw new FileNotFoundException("Could not find the XmlTv file specified:", info.Path);
- }
-
- return Task.FromResult(true);
- }
-
- public async Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
- {
- // In theory this should never be called because there is always only one lineup
- var path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false);
- _logger.Debug("Opening XmlTvReader for {0}", path);
- var reader = new XmlTvReader(path, GetLanguage(info));
- var results = reader.GetChannels();
-
- // Should this method be async?
- return results.Select(c => new NameIdPair() { Id = c.Id, Name = c.DisplayName }).ToList();
- }
-
- public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- // In theory this should never be called because there is always only one lineup
- var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- _logger.Debug("Opening XmlTvReader for {0}", path);
- var reader = new XmlTvReader(path, GetLanguage(info));
- var results = reader.GetChannels();
-
- // Should this method be async?
- return results.Select(c => new ChannelInfo
- {
- Id = c.Id,
- Name = c.DisplayName,
- ImageUrl = c.Icon != null && !String.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
- Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
-
- }).ToList();
- }
- }
-} \ No newline at end of file
diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
deleted file mode 100644
index 143350a8b..000000000
--- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-
-namespace Emby.Server.Implementations.LiveTv
-{
- public class LiveStreamHelper
- {
- private readonly IMediaEncoder _mediaEncoder;
- private readonly ILogger _logger;
-
- const int ProbeAnalyzeDurationMs = 3000;
- const int PlaybackAnalyzeDurationMs = 3000;
-
- public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
- {
- _mediaEncoder = mediaEncoder;
- _logger = logger;
- }
-
- public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
- {
- var originalRuntime = mediaSource.RunTimeTicks;
-
- var now = DateTime.UtcNow;
-
- var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
- {
- InputPath = mediaSource.Path,
- Protocol = mediaSource.Protocol,
- MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
- ExtractChapters = false,
- AnalyzeDurationMs = ProbeAnalyzeDurationMs
-
- }, cancellationToken).ConfigureAwait(false);
-
- _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
-
- mediaSource.Bitrate = info.Bitrate;
- mediaSource.Container = info.Container;
- mediaSource.Formats = info.Formats;
- mediaSource.MediaStreams = info.MediaStreams;
- mediaSource.RunTimeTicks = info.RunTimeTicks;
- mediaSource.Size = info.Size;
- mediaSource.Timestamp = info.Timestamp;
- mediaSource.Video3DFormat = info.Video3DFormat;
- mediaSource.VideoType = info.VideoType;
-
- mediaSource.DefaultSubtitleStreamIndex = null;
-
- // Null this out so that it will be treated like a live stream
- if (!originalRuntime.HasValue)
- {
- mediaSource.RunTimeTicks = null;
- }
-
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
-
- if (audioStream == null || audioStream.Index == -1)
- {
- mediaSource.DefaultAudioStreamIndex = null;
- }
- else
- {
- mediaSource.DefaultAudioStreamIndex = audioStream.Index;
- }
-
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
- if (videoStream != null)
- {
- if (!videoStream.BitRate.HasValue)
- {
- var width = videoStream.Width ?? 1920;
-
- if (width >= 3000)
- {
- videoStream.BitRate = 30000000;
- }
-
- else if (width >= 1900)
- {
- videoStream.BitRate = 20000000;
- }
-
- else if (width >= 1200)
- {
- videoStream.BitRate = 8000000;
- }
-
- else if (width >= 700)
- {
- videoStream.BitRate = 2000000;
- }
- }
-
- // This is coming up false and preventing stream copy
- videoStream.IsAVC = null;
- }
-
- // Try to estimate this
- mediaSource.InferTotalBitrate(true);
-
- mediaSource.AnalyzeDurationMs = PlaybackAnalyzeDurationMs;
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
index 2be642737..205a767eb 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -8,7 +8,7 @@ namespace Emby.Server.Implementations.LiveTv
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
- return new List<ConfigurationStore>
+ return new ConfigurationStore[]
{
new ConfigurationStore
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 15bbca136..56b3b5e4b 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Extensions;
+using System.Collections.Generic;
namespace Emby.Server.Implementations.LiveTv
{
@@ -36,19 +37,19 @@ namespace Emby.Server.Implementations.LiveTv
_libraryManager = libraryManager;
}
- public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel)
+ public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, BaseItem channel)
{
var dto = new TimerInfoDto
{
- Id = GetInternalTimerId(service.Name, info.Id).ToString("N"),
+ Id = GetInternalTimerId(info.Id),
Overview = info.Overview,
EndDate = info.EndDate,
Name = info.Name,
StartDate = info.StartDate,
ExternalId = info.Id,
- ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"),
+ ChannelId = GetInternalChannelId(service.Name, info.ChannelId),
Status = info.Status,
- SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"),
+ SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"),
PrePaddingSeconds = info.PrePaddingSeconds,
PostPaddingSeconds = info.PostPaddingSeconds,
IsPostPaddingRequired = info.IsPostPaddingRequired,
@@ -65,7 +66,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(info.ProgramId))
{
- dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
+ dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N");
}
if (program != null)
@@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.LiveTv
dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
- if (!string.IsNullOrWhiteSpace(info.SeriesTimerId))
+ if (!string.IsNullOrEmpty(info.SeriesTimerId))
{
FillImages(dto.ProgramInfo, info.Name, info.SeriesId);
}
@@ -103,7 +104,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var dto = new SeriesTimerInfoDto
{
- Id = GetInternalSeriesTimerId(service.Name, info.Id).ToString("N"),
+ Id = GetInternalSeriesTimerId(info.Id).ToString("N"),
Overview = info.Overview,
EndDate = info.EndDate,
Name = info.Name,
@@ -130,12 +131,12 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(info.ChannelId))
{
- dto.ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N");
+ dto.ChannelId = GetInternalChannelId(service.Name, info.ChannelId);
}
if (!string.IsNullOrEmpty(info.ProgramId))
{
- dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
+ dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N");
}
dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray(info.Days.Count));
@@ -188,49 +189,47 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- if (!string.IsNullOrWhiteSpace(programSeriesId))
+ var program = _libraryManager.GetItemList(new InternalItemsQuery
{
- var program = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
- ExternalSeriesId = programSeriesId,
- Limit = 1,
- ImageTypes = new ImageType[] { ImageType.Primary },
- DtoOptions = new DtoOptions(false)
+ IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ ExternalSeriesId = programSeriesId,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Primary },
+ DtoOptions = new DtoOptions(false),
+ Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null
- }).FirstOrDefault();
+ }).FirstOrDefault();
- if (program != null)
+ if (program != null)
+ {
+ var image = program.GetImageInfo(ImageType.Primary, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
+ dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
{
- var image = program.GetImageInfo(ImageType.Primary, 0);
+ image = program.GetImageInfo(ImageType.Backdrop, 0);
if (image != null)
{
try
{
- dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
- dto.ParentPrimaryImageItemId = program.Id.ToString("N");
- }
- catch (Exception ex)
+ dto.ParentBackdropImageTags = new string[]
{
+ _imageProcessor.GetImageCacheTag(program, image)
+ };
+ dto.ParentBackdropItemId = program.Id.ToString("N");
}
- }
-
- if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
- {
- image = program.GetImageInfo(ImageType.Backdrop, 0);
- if (image != null)
+ catch (Exception ex)
{
- try
- {
- dto.ParentBackdropImageTags = new string[]
- {
- _imageProcessor.GetImageCacheTag(program, image)
- };
- dto.ParentBackdropItemId = program.Id.ToString("N");
- }
- catch (Exception ex)
- {
- }
}
}
}
@@ -280,59 +279,62 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- if (!string.IsNullOrWhiteSpace(programSeriesId))
+ var program = _libraryManager.GetItemList(new InternalItemsQuery
{
- var program = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new string[] { typeof(Series).Name },
- Name = seriesName,
- Limit = 1,
- ImageTypes = new ImageType[] { ImageType.Primary },
- DtoOptions = new DtoOptions(false)
+ IncludeItemTypes = new string[] { typeof(Series).Name },
+ Name = seriesName,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Primary },
+ DtoOptions = new DtoOptions(false)
+
+ }).FirstOrDefault();
- }).FirstOrDefault() ?? _libraryManager.GetItemList(new InternalItemsQuery
+ if (program == null)
+ {
+ program = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary },
- DtoOptions = new DtoOptions(false)
+ DtoOptions = new DtoOptions(false),
+ Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null
}).FirstOrDefault();
+ }
- if (program != null)
+ if (program != null)
+ {
+ var image = program.GetImageInfo(ImageType.Primary, 0);
+ if (image != null)
{
- var image = program.GetImageInfo(ImageType.Primary, 0);
+ try
+ {
+ dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
+ dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
+ {
+ image = program.GetImageInfo(ImageType.Backdrop, 0);
if (image != null)
{
try
{
- dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
- dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+ dto.ParentBackdropImageTags = new string[]
+ {
+ _imageProcessor.GetImageCacheTag(program, image)
+ };
+ dto.ParentBackdropItemId = program.Id.ToString("N");
}
catch (Exception ex)
{
}
}
-
- if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
- {
- image = program.GetImageInfo(ImageType.Backdrop, 0);
- if (image != null)
- {
- try
- {
- dto.ParentBackdropImageTags = new string[]
- {
- _imageProcessor.GetImageCacheTag(program, image)
- };
- dto.ParentBackdropItemId = program.Id.ToString("N");
- }
- catch (Exception ex)
- {
- }
- }
- }
}
}
}
@@ -366,35 +368,7 @@ namespace Emby.Server.Implementations.LiveTv
return pattern;
}
- public LiveTvTunerInfoDto GetTunerInfoDto(string serviceName, LiveTvTunerInfo info, string channelName)
- {
- var dto = new LiveTvTunerInfoDto
- {
- Name = info.Name,
- Id = info.Id,
- Clients = info.Clients.ToArray(),
- ProgramName = info.ProgramName,
- SourceType = info.SourceType,
- Status = info.Status,
- ChannelName = channelName,
- Url = info.Url,
- CanReset = info.CanReset
- };
-
- if (!string.IsNullOrEmpty(info.ChannelId))
- {
- dto.ChannelId = GetInternalChannelId(serviceName, info.ChannelId).ToString("N");
- }
-
- if (!string.IsNullOrEmpty(info.RecordingId))
- {
- dto.RecordingId = GetInternalRecordingId(serviceName, info.RecordingId).ToString("N");
- }
-
- return dto;
- }
-
- internal string GetImageTag(IHasMetadata info)
+ internal string GetImageTag(BaseItem info)
{
try
{
@@ -417,46 +391,28 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvChannel));
}
- public Guid GetInternalTimerId(string serviceName, string externalId)
+ private const string ServiceName = "Emby";
+ public string GetInternalTimerId(string externalId)
{
- var name = serviceName + externalId + InternalVersionNumber;
+ var name = ServiceName + externalId + InternalVersionNumber;
- return name.ToLower().GetMD5();
+ return name.ToLower().GetMD5().ToString("N");
}
- public Guid GetInternalSeriesTimerId(string serviceName, string externalId)
+ public Guid GetInternalSeriesTimerId(string externalId)
{
- var name = serviceName + externalId + InternalVersionNumber;
+ var name = ServiceName + externalId + InternalVersionNumber;
return name.ToLower().GetMD5();
}
- public Guid GetInternalProgramId(string serviceName, string externalId)
+ public Guid GetInternalProgramId(string externalId)
{
- var name = serviceName + externalId + InternalVersionNumber;
+ var name = ServiceName + externalId + InternalVersionNumber;
return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvProgram));
}
- public Guid GetInternalRecordingId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber + "0";
-
- return _libraryManager.GetNewItemId(name.ToLower(), typeof(ILiveTvRecording));
- }
-
- private string GetItemExternalId(BaseItem item)
- {
- var externalId = item.ExternalId;
-
- if (string.IsNullOrWhiteSpace(externalId))
- {
- externalId = item.GetProviderId("ProviderExternalId");
- }
-
- return externalId;
- }
-
public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken)
{
var info = new TimerInfo
@@ -486,23 +442,23 @@ namespace Emby.Server.Implementations.LiveTv
info.Id = timer.ExternalId;
}
- if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.Equals(Guid.Empty) && string.IsNullOrEmpty(info.ChannelId))
{
- var channel = liveTv.GetInternalChannel(dto.ChannelId);
+ var channel = _libraryManager.GetItemById(dto.ChannelId);
if (channel != null)
{
- info.ChannelId = GetItemExternalId(channel);
+ info.ChannelId = channel.ExternalId;
}
}
if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
{
- var program = liveTv.GetInternalProgram(dto.ProgramId);
+ var program = _libraryManager.GetItemById(dto.ProgramId);
if (program != null)
{
- info.ProgramId = GetItemExternalId(program);
+ info.ProgramId = program.ExternalId;
}
}
@@ -552,23 +508,23 @@ namespace Emby.Server.Implementations.LiveTv
info.Id = timer.ExternalId;
}
- if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.Equals(Guid.Empty) && string.IsNullOrEmpty(info.ChannelId))
{
- var channel = liveTv.GetInternalChannel(dto.ChannelId);
+ var channel = _libraryManager.GetItemById(dto.ChannelId);
if (channel != null)
{
- info.ChannelId = GetItemExternalId(channel);
+ info.ChannelId = channel.ExternalId;
}
}
if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
{
- var program = liveTv.GetInternalProgram(dto.ProgramId);
+ var program = _libraryManager.GetItemById(dto.ProgramId);
if (program != null)
{
- info.ProgramId = GetItemExternalId(program);
+ info.ProgramId = program.ExternalId;
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 211e0de4b..9cdf105d7 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -26,7 +26,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
-
using MediaBrowser.Common.Security;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
@@ -36,6 +35,10 @@ using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Emby.Server.Implementations.LiveTv.Listings;
+using MediaBrowser.Controller.Channels;
+using Emby.Server.Implementations.Library;
+using MediaBrowser.Controller;
+using MediaBrowser.Common.Net;
namespace Emby.Server.Implementations.LiveTv
{
@@ -54,6 +57,7 @@ namespace Emby.Server.Implementations.LiveTv
private readonly IJsonSerializer _jsonSerializer;
private readonly IProviderManager _providerManager;
private readonly ISecurityManager _security;
+ private readonly Func<IChannelManager> _channelManager;
private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization;
@@ -62,10 +66,8 @@ namespace Emby.Server.Implementations.LiveTv
private ILiveTvService[] _services = new ILiveTvService[] { };
- private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
-
- private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>();
- private readonly List<IListingsProvider> _listingProviders = new List<IListingsProvider>();
+ private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
+ private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
private readonly IFileSystem _fileSystem;
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
@@ -78,13 +80,12 @@ namespace Emby.Server.Implementations.LiveTv
return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
}
- public Task<ILiveStream> GetEmbyTvLiveStream(string id)
- {
- return EmbyTV.EmbyTV.Current.GetLiveStream(id);
- }
+ private IServerApplicationHost _appHost;
+ private IHttpClient _httpClient;
- public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security)
+ public LiveTvManager(IServerApplicationHost appHost, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security, Func<IChannelManager> channelManager)
{
+ _appHost = appHost;
_config = config;
_logger = logger;
_itemRepo = itemRepo;
@@ -98,6 +99,8 @@ namespace Emby.Server.Implementations.LiveTv
_security = security;
_dtoService = dtoService;
_userDataManager = userDataManager;
+ _channelManager = channelManager;
+ _httpClient = httpClient;
_tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, logger, appHost, _libraryManager);
}
@@ -125,27 +128,58 @@ namespace Emby.Server.Implementations.LiveTv
public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
{
_services = services.ToArray();
- _tunerHosts.AddRange(tunerHosts.Where(i => i.IsSupported));
- _listingProviders.AddRange(listingProviders);
+ _tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray();
+
+ _listingProviders = listingProviders.ToArray();
foreach (var service in _services)
{
service.DataSourceChanged += service_DataSourceChanged;
- service.RecordingStatusChanged += Service_RecordingStatusChanged;
+
+ var embyTv = service as EmbyTV.EmbyTV;
+
+ if (embyTv != null)
+ {
+ embyTv.TimerCreated += EmbyTv_TimerCreated;
+ embyTv.TimerCancelled += EmbyTv_TimerCancelled;
+ }
}
}
- private void Service_RecordingStatusChanged(object sender, RecordingStatusChangedEventArgs e)
+ private void EmbyTv_TimerCancelled(object sender, GenericEventArgs<string> e)
{
- _lastRecordingRefreshTime = DateTime.MinValue;
+ var timerId = e.Argument;
+
+ EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
+ {
+ Argument = new TimerEventInfo
+ {
+ Id = timerId
+ }
+ }, _logger);
+ }
+
+ private void EmbyTv_TimerCreated(object sender, GenericEventArgs<TimerInfo> e)
+ {
+ var timer = e.Argument;
+ var service = sender as ILiveTvService;
+
+ EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
+ {
+ Argument = new TimerEventInfo
+ {
+ ProgramId = _tvDtoService.GetInternalProgramId(timer.ProgramId),
+ Id = timer.Id
+ }
+ }, _logger);
}
- public List<ITunerHost> TunerHosts
+ public ITunerHost[] TunerHosts
{
get { return _tunerHosts; }
}
- public List<IListingsProvider> ListingProviders
+ public IListingsProvider[] ListingProviders
{
get { return _listingProviders; }
}
@@ -175,7 +209,7 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
var topFolder = GetInternalLiveTvFolder(cancellationToken);
@@ -187,7 +221,7 @@ namespace Emby.Server.Implementations.LiveTv
IsSports = query.IsSports,
IsSeries = query.IsSeries,
IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
- TopParentIds = new[] { topFolder.Id.ToString("N") },
+ TopParentIds = new[] { topFolder.Id },
IsFavorite = query.IsFavorite,
IsLiked = query.IsLiked,
StartIndex = query.StartIndex,
@@ -197,16 +231,16 @@ namespace Emby.Server.Implementations.LiveTv
var orderBy = internalQuery.OrderBy.ToList();
- orderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
+ orderBy.AddRange(query.SortBy.Select(i => new ValueTuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
if (query.EnableFavoriteSorting)
{
- orderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
+ orderBy.Insert(0, new ValueTuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
}
if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
{
- orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
+ orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
}
internalQuery.OrderBy = orderBy.ToArray();
@@ -214,75 +248,54 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetItemsResult(internalQuery);
}
- public LiveTvChannel GetInternalChannel(string id)
- {
- return GetInternalChannel(new Guid(id));
- }
-
- private LiveTvChannel GetInternalChannel(Guid id)
+ public async Task<Tuple<MediaSourceInfo, ILiveStream>> GetChannelStream(string id, string mediaSourceId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- return _libraryManager.GetItemById(id) as LiveTvChannel;
- }
+ if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
+ {
+ mediaSourceId = null;
+ }
- internal LiveTvProgram GetInternalProgram(string id)
- {
- return _libraryManager.GetItemById(id) as LiveTvProgram;
- }
+ MediaSourceInfo info;
+ bool isVideo;
+ ILiveTvService service;
+ ILiveStream liveStream;
- internal LiveTvProgram GetInternalProgram(Guid id)
- {
- return _libraryManager.GetItemById(id) as LiveTvProgram;
- }
+ var channel = (LiveTvChannel)_libraryManager.GetItemById(id);
+ isVideo = channel.ChannelType == ChannelType.TV;
+ service = GetService(channel);
+ _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
- public async Task<BaseItem> GetInternalRecording(string id, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(id))
+ var supportsManagedStream = service as ISupportsDirectStreamProvider;
+ if (supportsManagedStream != null)
{
- throw new ArgumentNullException("id");
+ liveStream = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
+ info = liveStream.MediaSource;
}
-
- var result = await GetInternalRecordings(new RecordingQuery
+ else
{
- Id = id
-
- }, new DtoOptions(), cancellationToken).ConfigureAwait(false);
+ info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+ var openedId = info.Id;
+ Func<Task> closeFn = () => service.CloseLiveStream(openedId, CancellationToken.None);
- return result.Items.FirstOrDefault();
- }
-
- public async Task<MediaSourceInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
- {
- var info = await GetLiveStream(id, null, false, cancellationToken).ConfigureAwait(false);
-
- return info.Item1;
- }
-
- public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken)
- {
- return GetLiveStream(id, mediaSourceId, true, cancellationToken);
- }
-
- private string GetItemExternalId(BaseItem item)
- {
- var externalId = item.ExternalId;
+ liveStream = new ExclusiveLiveStream(info, closeFn);
- if (string.IsNullOrWhiteSpace(externalId))
- {
- externalId = item.GetProviderId("ProviderExternalId");
+ var startTime = DateTime.UtcNow;
+ await liveStream.Open(cancellationToken).ConfigureAwait(false);
+ var endTime = DateTime.UtcNow;
+ _logger.Info("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
}
+ info.RequiresClosing = true;
- return externalId;
- }
+ var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
- public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var baseItem = (BaseItem)item;
- var service = GetService(baseItem);
+ info.LiveStreamId = idPrefix + info.Id;
+
+ Normalize(info, service, isVideo);
- return await service.GetRecordingStreamMediaSources(GetItemExternalId(baseItem), cancellationToken).ConfigureAwait(false);
+ return new Tuple<MediaSourceInfo, ILiveStream>(info, liveStream);
}
- public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(BaseItem item, CancellationToken cancellationToken)
{
var baseItem = (LiveTvChannel)item;
var service = GetService(baseItem);
@@ -304,14 +317,17 @@ namespace Emby.Server.Implementations.LiveTv
return list;
}
- private ILiveTvService GetService(ILiveTvRecording item)
+ private ILiveTvService GetService(LiveTvChannel item)
{
- return GetService(item.ServiceName);
+ var name = item.ServiceName;
+ return GetService(name);
}
- private ILiveTvService GetService(BaseItem item)
+ private ILiveTvService GetService(LiveTvProgram item)
{
- return GetService(item.ServiceName);
+ var channel = _libraryManager.GetItemById(item.ChannelId) as LiveTvChannel;
+
+ return GetService(channel);
}
private ILiveTvService GetService(string name)
@@ -319,70 +335,11 @@ namespace Emby.Server.Implementations.LiveTv
return _services.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
}
- private async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken)
- {
- if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
- {
- mediaSourceId = null;
- }
-
- MediaSourceInfo info;
- bool isVideo;
- ILiveTvService service;
- IDirectStreamProvider directStreamProvider = null;
-
- if (isChannel)
- {
- var channel = GetInternalChannel(id);
- isVideo = channel.ChannelType == ChannelType.TV;
- service = GetService(channel);
- _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, GetItemExternalId(channel));
-
- var supportsManagedStream = service as ISupportsDirectStreamProvider;
- if (supportsManagedStream != null)
- {
- var streamInfo = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(GetItemExternalId(channel), mediaSourceId, cancellationToken).ConfigureAwait(false);
- info = streamInfo.Item1;
- directStreamProvider = streamInfo.Item2;
- }
- else
- {
- info = await service.GetChannelStream(GetItemExternalId(channel), mediaSourceId, cancellationToken).ConfigureAwait(false);
- }
- info.RequiresClosing = true;
-
- if (info.RequiresClosing)
- {
- var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- info.LiveStreamId = idPrefix + info.Id;
- }
- }
- else
- {
- var recording = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
- isVideo = !string.Equals(recording.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase);
- service = GetService(recording);
-
- _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, GetItemExternalId(recording));
- info = await service.GetRecordingStream(GetItemExternalId(recording), null, cancellationToken).ConfigureAwait(false);
- info.RequiresClosing = true;
-
- if (info.RequiresClosing)
- {
- var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- info.LiveStreamId = idPrefix + info.Id;
- }
- }
-
- Normalize(info, service, isVideo);
-
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info, directStreamProvider);
- }
-
private void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo)
{
+ // Not all of the plugins are setting this
+ mediaSource.IsInfiniteStream = true;
+
if (mediaSource.MediaStreams.Count == 0)
{
if (isVideo)
@@ -475,7 +432,7 @@ namespace Emby.Server.Implementations.LiveTv
{
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
//mediaSource.SupportsDirectPlay = false;
- mediaSource.SupportsDirectStream = false;
+ //mediaSource.SupportsDirectStream = false;
mediaSource.SupportsTranscoding = true;
foreach (var stream in mediaSource.MediaStreams)
{
@@ -492,8 +449,10 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
+ private const string ExternalServiceTag = "ExternalServiceId";
+ private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{
+ var parentFolderId = parentFolder.Id;
var isNew = false;
var forceUpdate = false;
@@ -507,17 +466,20 @@ namespace Emby.Server.Implementations.LiveTv
{
Name = channelInfo.Name,
Id = id,
- DateCreated = DateTime.UtcNow,
+ DateCreated = DateTime.UtcNow
};
isNew = true;
}
- if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
+ if (channelInfo.Tags != null)
{
- isNew = true;
+ if (!channelInfo.Tags.SequenceEqual(item.Tags, StringComparer.OrdinalIgnoreCase))
+ {
+ isNew = true;
+ }
+ item.Tags = channelInfo.Tags;
}
- item.ExternalId = channelInfo.Id;
if (!item.ParentId.Equals(parentFolderId))
{
@@ -528,6 +490,18 @@ namespace Emby.Server.Implementations.LiveTv
item.ChannelType = channelInfo.ChannelType;
item.ServiceName = serviceName;
+ if (!string.Equals(item.GetProviderId(ExternalServiceTag), serviceName, StringComparison.OrdinalIgnoreCase))
+ {
+ forceUpdate = true;
+ }
+ item.SetProviderId(ExternalServiceTag, serviceName);
+
+ if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
+ {
+ forceUpdate = true;
+ }
+ item.ExternalId = channelInfo.Id;
+
if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
{
forceUpdate = true;
@@ -556,25 +530,21 @@ namespace Emby.Server.Implementations.LiveTv
if (isNew)
{
- _libraryManager.CreateItem(item, cancellationToken);
+ _libraryManager.CreateItem(item, parentFolder);
}
else if (forceUpdate)
{
- _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken);
+ _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
}
- await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
- {
- ForceSave = isNew || forceUpdate
-
- }, cancellationToken);
-
return item;
}
+ private const string EtagKey = "ProgramEtag";
+
private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
{
- var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
+ var id = _tvDtoService.GetInternalProgramId(info.Id);
LiveTvProgram item = null;
allExistingPrograms.TryGetValue(id, out item);
@@ -590,9 +560,13 @@ namespace Emby.Server.Implementations.LiveTv
Name = info.Name,
Id = id,
DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- ExternalEtag = info.Etag
+ DateModified = DateTime.UtcNow
};
+
+ if (!string.IsNullOrEmpty(info.Etag))
+ {
+ item.SetProviderId(EtagKey, info.Etag);
+ }
}
if (!string.Equals(info.ShowId, item.ShowId, StringComparison.OrdinalIgnoreCase))
@@ -610,10 +584,9 @@ namespace Emby.Server.Implementations.LiveTv
item.ParentId = channel.Id;
//item.ChannelType = channelType;
- item.ServiceName = serviceName;
item.Audio = info.Audio;
- item.ChannelId = channel.Id.ToString("N");
+ item.ChannelId = channel.Id;
item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
if ((item.CommunityRating ?? 0).Equals(0))
{
@@ -629,20 +602,76 @@ namespace Emby.Server.Implementations.LiveTv
}
item.ExternalSeriesId = seriesId;
- item.Genres = info.Genres;
- item.IsHD = info.IsHD;
- item.IsKids = info.IsKids;
- item.IsLive = info.IsLive;
+ var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
+
+ if (isSeries || !string.IsNullOrEmpty(info.EpisodeTitle))
+ {
+ item.SeriesName = info.Name;
+ }
+
+ var tags = new List<string>();
+ if (info.IsLive)
+ {
+ tags.Add("Live");
+ }
+ if (info.IsPremiere)
+ {
+ tags.Add("Premiere");
+ }
+ if (info.IsNews)
+ {
+ tags.Add("News");
+ }
+ if (info.IsSports)
+ {
+ tags.Add("Sports");
+ }
+ if (info.IsKids)
+ {
+ tags.Add("Kids");
+ }
+ if (info.IsRepeat)
+ {
+ tags.Add("Repeat");
+ }
+ if (info.IsMovie)
+ {
+ tags.Add("Movie");
+ }
+ if (isSeries)
+ {
+ tags.Add("Series");
+ }
+
+ item.Tags = tags.ToArray();
+
+ item.Genres = info.Genres.ToArray();
+
+ if (info.IsHD ?? false)
+ {
+ item.Width = 1280;
+ item.Height = 720;
+ }
+
item.IsMovie = info.IsMovie;
- item.IsNews = info.IsNews;
- item.IsPremiere = info.IsPremiere;
item.IsRepeat = info.IsRepeat;
- item.IsSeries = info.IsSeries;
- item.IsSports = info.IsSports;
+
+ if (item.IsSeries != isSeries)
+ {
+ forceUpdate = true;
+ }
+ item.IsSeries = isSeries;
+
item.Name = info.Name;
item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
item.Overview = item.Overview ?? info.Overview;
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
+ item.ProviderIds = info.ProviderIds;
+
+ foreach (var providerId in info.SeriesProviderIds)
+ {
+ info.ProviderIds["Series" + providerId.Key] = providerId.Value;
+ }
if (item.StartDate != info.StartDate)
{
@@ -656,11 +685,9 @@ namespace Emby.Server.Implementations.LiveTv
}
item.EndDate = info.EndDate;
- item.HomePageUrl = info.HomePageUrl;
-
item.ProductionYear = info.ProductionYear;
- if (!info.IsSeries || info.IsRepeat)
+ if (!isSeries || info.IsRepeat)
{
item.PremiereDate = info.OriginalAirDate;
}
@@ -737,12 +764,11 @@ namespace Emby.Server.Implementations.LiveTv
}
else
{
- // Increment this whenver some internal change deems it necessary
- var etag = info.Etag + "6";
+ var etag = info.Etag;
- if (!string.Equals(etag, item.ExternalEtag, StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(etag, item.GetProviderId(EtagKey), StringComparison.OrdinalIgnoreCase))
{
- item.ExternalEtag = etag;
+ item.SetProviderId(EtagKey, etag);
isUpdated = true;
}
}
@@ -755,180 +781,47 @@ namespace Emby.Server.Implementations.LiveTv
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
}
- private Guid CreateRecordingRecord(RecordingInfo info, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
- {
- var isNew = false;
-
- var id = _tvDtoService.GetInternalRecordingId(serviceName, info.Id);
-
- var item = _itemRepo.RetrieveItem(id);
-
- if (item == null)
- {
- if (info.ChannelType == ChannelType.TV)
- {
- item = new LiveTvVideoRecording
- {
- Name = info.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- VideoType = VideoType.VideoFile
- };
- }
- else
- {
- item = new LiveTvAudioRecording
- {
- Name = info.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
- };
- }
-
- isNew = true;
- }
-
- item.ChannelId = _tvDtoService.GetInternalChannelId(serviceName, info.ChannelId).ToString("N");
- item.CommunityRating = info.CommunityRating;
- item.OfficialRating = info.OfficialRating;
- item.Overview = info.Overview;
- item.EndDate = info.EndDate;
- item.Genres = info.Genres;
- item.PremiereDate = info.OriginalAirDate;
-
- var recording = (ILiveTvRecording)item;
-
- recording.ExternalId = info.Id;
-
- var dataChanged = false;
-
- recording.Audio = info.Audio;
- recording.EndDate = info.EndDate;
- recording.EpisodeTitle = info.EpisodeTitle;
- recording.IsHD = info.IsHD;
- recording.IsKids = info.IsKids;
- recording.IsLive = info.IsLive;
- recording.IsMovie = info.IsMovie;
- recording.IsNews = info.IsNews;
- recording.IsPremiere = info.IsPremiere;
- recording.IsRepeat = info.IsRepeat;
- recording.IsSports = info.IsSports;
- recording.SeriesTimerId = info.SeriesTimerId;
- recording.TimerId = info.TimerId;
- recording.StartDate = info.StartDate;
-
- if (!dataChanged)
- {
- dataChanged = recording.IsSeries != info.IsSeries;
- }
- recording.IsSeries = info.IsSeries;
-
- if (!item.ParentId.Equals(parentFolderId))
- {
- dataChanged = true;
- }
- item.ParentId = parentFolderId;
-
- if (!item.HasImage(ImageType.Primary))
- {
- if (!string.IsNullOrWhiteSpace(info.ImagePath))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImagePath,
- Type = ImageType.Primary
- }, 0);
- }
- else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImageUrl,
- Type = ImageType.Primary
- }, 0);
- }
- }
-
- var statusChanged = info.Status != recording.Status;
-
- recording.Status = info.Status;
-
- recording.ServiceName = serviceName;
-
- if (!string.IsNullOrEmpty(info.Path))
- {
- if (!dataChanged)
- {
- dataChanged = !string.Equals(item.Path, info.Path);
- }
- var fileInfo = _fileSystem.GetFileInfo(info.Path);
-
- recording.DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo);
- recording.DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo);
- item.Path = info.Path;
- }
- else if (!string.IsNullOrEmpty(info.Url))
- {
- if (!dataChanged)
- {
- dataChanged = !string.Equals(item.Path, info.Url);
- }
- item.Path = info.Url;
- }
-
- var metadataRefreshMode = MetadataRefreshMode.Default;
-
- if (isNew)
- {
- _libraryManager.CreateItem(item, cancellationToken);
- }
- else if (dataChanged || info.DateLastUpdated > recording.DateLastSaved || statusChanged)
- {
- metadataRefreshMode = MetadataRefreshMode.FullRefresh;
- _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken);
- }
-
- if (info.Status != RecordingStatus.InProgress)
- {
- _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- MetadataRefreshMode = metadataRefreshMode
-
- }, RefreshPriority.Normal);
- }
-
- return item.Id;
- }
-
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{
- var program = GetInternalProgram(id);
+ var program = _libraryManager.GetItemById(id);
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
- var list = new List<Tuple<BaseItemDto, string, string, string>>();
+ var list = new List<Tuple<BaseItemDto, string, string>>();
var externalSeriesId = program.ExternalSeriesId;
- list.Add(new Tuple<BaseItemDto, string, string, string>(dto, program.ServiceName, GetItemExternalId(program), externalSeriesId));
+ list.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, externalSeriesId));
await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
return dto;
}
- public async Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItemDto>> GetPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
{
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.User;
var topFolder = GetInternalLiveTvFolder(cancellationToken);
if (query.OrderBy.Length == 0)
{
- // Unless something else was specified, order by start date to take advantage of a specialized index
- query.OrderBy = new Tuple<string, SortOrder>[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) };
+ if (query.IsAiring ?? false)
+ {
+ // Unless something else was specified, order by start date to take advantage of a specialized index
+ query.OrderBy = new ValueTuple<string, SortOrder>[]
+ {
+ new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending)
+ };
+ }
+ else
+ {
+ // Unless something else was specified, order by start date to take advantage of a specialized index
+ query.OrderBy = new ValueTuple<string, SortOrder>[]
+ {
+ new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending)
+ };
+ }
}
RemoveFields(options);
@@ -952,15 +845,17 @@ namespace Emby.Server.Implementations.LiveTv
Limit = query.Limit,
OrderBy = query.OrderBy,
EnableTotalRecordCount = query.EnableTotalRecordCount,
- TopParentIds = new[] { topFolder.Id.ToString("N") },
+ TopParentIds = new[] { topFolder.Id },
Name = query.Name,
- DtoOptions = options
+ DtoOptions = options,
+ HasAired = query.HasAired,
+ IsAiring = query.IsAiring
};
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
- var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
+ var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
@@ -978,18 +873,6 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- if (query.HasAired.HasValue)
- {
- if (query.HasAired.Value)
- {
- internalQuery.MaxEndDate = DateTime.UtcNow;
- }
- else
- {
- internalQuery.MinEndDate = DateTime.UtcNow;
- }
- }
-
var queryResult = _libraryManager.QueryItems(internalQuery);
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
@@ -1003,9 +886,9 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public QueryResult<BaseItem> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
{
- var user = _userManager.GetUserById(query.UserId);
+ var user = query.User;
var topFolder = GetInternalLiveTvFolder(cancellationToken);
@@ -1013,14 +896,15 @@ namespace Emby.Server.Implementations.LiveTv
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
IsAiring = query.IsAiring,
+ HasAired = query.HasAired,
IsNews = query.IsNews,
IsMovie = query.IsMovie,
IsSeries = query.IsSeries,
IsSports = query.IsSports,
IsKids = query.IsKids,
EnableTotalRecordCount = query.EnableTotalRecordCount,
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
- TopParentIds = new[] { topFolder.Id.ToString("N") },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
+ TopParentIds = new[] { topFolder.Id },
DtoOptions = options,
GenreIds = query.GenreIds
};
@@ -1030,18 +914,6 @@ namespace Emby.Server.Implementations.LiveTv
internalQuery.Limit = Math.Max(query.Limit.Value * 4, 200);
}
- if (query.HasAired.HasValue)
- {
- if (query.HasAired.Value)
- {
- internalQuery.MaxEndDate = DateTime.UtcNow;
- }
- else
- {
- internalQuery.MinEndDate = DateTime.UtcNow;
- }
- }
-
var programList = _libraryManager.QueryItems(internalQuery).Items;
var totalCount = programList.Length;
@@ -1050,7 +922,7 @@ namespace Emby.Server.Implementations.LiveTv
if (query.IsAiring ?? false)
{
orderedPrograms = orderedPrograms
- .ThenByDescending(i => GetRecommendationScore(i, user.Id, true));
+ .ThenByDescending(i => GetRecommendationScore(i, user, true));
}
IEnumerable<BaseItem> programs = orderedPrograms;
@@ -1069,13 +941,18 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public QueryResult<BaseItemDto> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
{
+ if (!(query.IsAiring ?? false))
+ {
+ return GetPrograms(query, options, cancellationToken).Result;
+ }
+
RemoveFields(options);
var internalResult = GetRecommendedProgramsInternal(query, options, cancellationToken);
- var user = _userManager.GetUserById(query.UserId);
+ var user = query.User;
var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
@@ -1088,7 +965,7 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- private int GetRecommendationScore(LiveTvProgram program, Guid userId, bool factorChannelWatchCount)
+ private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
{
var score = 0;
@@ -1102,11 +979,11 @@ namespace Emby.Server.Implementations.LiveTv
score++;
}
- var channel = GetInternalChannel(program.ChannelId);
+ var channel = _libraryManager.GetItemById(program.ChannelId);
if (channel != null)
{
- var channelUserdata = _userDataManager.GetUserData(userId, channel);
+ var channelUserdata = _userDataManager.GetUserData(user, channel);
if (channelUserdata.Likes ?? false)
{
@@ -1131,36 +1008,23 @@ namespace Emby.Server.Implementations.LiveTv
return score;
}
- private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string, string>> programs, CancellationToken cancellationToken)
+ private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
{
var timers = new Dictionary<string, List<TimerInfo>>();
var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
+ TimerInfo[] timerList = null;
+ SeriesTimerInfo[] seriesTimerList = null;
+
foreach (var programTuple in programs)
{
var program = programTuple.Item1;
- var serviceName = programTuple.Item2;
- var externalProgramId = programTuple.Item3;
- string externalSeriesId = programTuple.Item4;
+ var externalProgramId = programTuple.Item2;
+ string externalSeriesId = programTuple.Item3;
- if (string.IsNullOrWhiteSpace(serviceName))
+ if (timerList == null)
{
- continue;
- }
-
- List<TimerInfo> timerList;
- if (!timers.TryGetValue(serviceName, out timerList))
- {
- try
- {
- var tempTimers = await GetService(serviceName).GetTimersAsync(cancellationToken).ConfigureAwait(false);
- timers[serviceName] = timerList = tempTimers.ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting timer infos", ex);
- timers[serviceName] = timerList = new List<TimerInfo>();
- }
+ timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
}
var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
@@ -1170,15 +1034,14 @@ namespace Emby.Server.Implementations.LiveTv
{
if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
{
- program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
- .ToString("N");
+ program.TimerId = _tvDtoService.GetInternalTimerId(timer.Id);
program.Status = timer.Status.ToString();
}
if (!string.IsNullOrEmpty(timer.SeriesTimerId))
{
- program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, timer.SeriesTimerId)
+ program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
.ToString("N");
foundSeriesTimer = true;
@@ -1190,26 +1053,16 @@ namespace Emby.Server.Implementations.LiveTv
continue;
}
- List<SeriesTimerInfo> seriesTimerList;
- if (!seriesTimers.TryGetValue(serviceName, out seriesTimerList))
+ if (seriesTimerList == null)
{
- try
- {
- var tempTimers = await GetService(serviceName).GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
- seriesTimers[serviceName] = seriesTimerList = tempTimers.ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting series timer infos", ex);
- seriesTimers[serviceName] = seriesTimerList = new List<SeriesTimerInfo>();
- }
+ seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
}
var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
- program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, seriesTimer.Id)
+ program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id)
.ToString("N");
}
}
@@ -1222,7 +1075,7 @@ namespace Emby.Server.Implementations.LiveTv
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
- EmbyTV.EmbyTV.Current.CreateRecordingFolders();
+ await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
@@ -1271,8 +1124,8 @@ namespace Emby.Server.Implementations.LiveTv
if (cleanDatabase)
{
- await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
- await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
+ CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken);
+ CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken);
}
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
@@ -1286,12 +1139,9 @@ namespace Emby.Server.Implementations.LiveTv
// Load these now which will prefetch metadata
var dtoOptions = new DtoOptions();
var fields = dtoOptions.Fields.ToList();
- fields.Remove(ItemFields.SyncInfo);
fields.Remove(ItemFields.BasicSyncInfo);
dtoOptions.Fields = fields.ToArray(fields.Count);
- await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
-
progress.Report(100);
}
@@ -1315,7 +1165,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
- var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolderId, cancellationToken).ConfigureAwait(false);
+ var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
list.Add(item);
}
@@ -1363,19 +1213,19 @@ namespace Emby.Server.Implementations.LiveTv
var isKids = false;
var iSSeries = false;
- var channelPrograms = (await service.GetProgramsAsync(GetItemExternalId(currentChannel), start, end, cancellationToken).ConfigureAwait(false)).ToList();
+ var channelPrograms = (await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false)).ToList();
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
- ChannelIds = new string[] { currentChannel.Id.ToString("N") },
+ ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true)
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
var newPrograms = new List<LiveTvProgram>();
- var updatedPrograms = new List<LiveTvProgram>();
+ var updatedPrograms = new List<BaseItem>();
foreach (var program in channelPrograms)
{
@@ -1426,19 +1276,27 @@ namespace Emby.Server.Implementations.LiveTv
_libraryManager.CreateItems(newPrograms, null, cancellationToken);
}
- // TODO: Do this in bulk
- foreach (var program in updatedPrograms)
+ if (updatedPrograms.Count > 0)
{
- _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken);
+ _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
}
currentChannel.IsMovie = isMovie;
currentChannel.IsNews = isNews;
currentChannel.IsSports = isSports;
- currentChannel.IsKids = isKids;
currentChannel.IsSeries = iSSeries;
- currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+ if (isKids)
+ {
+ currentChannel.AddTag("Kids");
+ }
+
+ //currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+ await currentChannel.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
+ {
+ ForceSave = true
+
+ }, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -1455,12 +1313,12 @@ namespace Emby.Server.Implementations.LiveTv
progress.Report(85 * percent + 15);
}
- progress.Report(100);
+ progress.Report(100);
return new Tuple<List<Guid>, List<Guid>>(channels, programs);
}
- private async Task CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
+ private void CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
{
var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
{
@@ -1475,7 +1333,7 @@ namespace Emby.Server.Implementations.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
- if (itemId == Guid.Empty)
+ if (itemId.Equals(Guid.Empty))
{
// Somehow some invalid data got into the db. It probably predates the boundary checking
continue;
@@ -1487,11 +1345,12 @@ namespace Emby.Server.Implementations.LiveTv
if (item != null)
{
- await _libraryManager.DeleteItem(item, new DeleteOptions
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
- DeleteFileLocation = false
+ DeleteFileLocation = false,
+ DeleteFromExternalProvider = false
- }).ConfigureAwait(false);
+ }, false);
}
}
@@ -1516,72 +1375,19 @@ namespace Emby.Server.Implementations.LiveTv
return 7;
}
- private DateTime _lastRecordingRefreshTime;
- private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken)
- {
- const int cacheMinutes = 2;
-
- await _refreshRecordingsLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
- {
- return;
- }
-
- var tasks = _services.Select(async i =>
- {
- try
- {
- var recs = await i.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
- return recs.Select(r => new Tuple<RecordingInfo, ILiveTvService>(r, i));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recordings", ex);
- return new List<Tuple<RecordingInfo, ILiveTvService>>();
- }
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- var idList = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, internalLiveTvFolderId, cancellationToken))
- .ToArray();
-
- await CleanDatabaseInternal(idList, new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
-
- _lastRecordingRefreshTime = DateTime.UtcNow;
- }
- finally
- {
- _refreshRecordingsLock.Release();
- }
- }
-
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, Guid internalLiveTvFolderId, User user)
+ private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
{
if (user == null)
{
return new QueryResult<BaseItem>();
}
- var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
- .SelectMany(i => i.Locations)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
+ var folderIds = GetRecordingFolders(user, true)
.Select(i => i.Id)
.ToList();
var excludeItemTypes = new List<string>();
- folderIds.Add(internalLiveTvFolderId);
-
- excludeItemTypes.Add(typeof(LiveTvChannel).Name);
- excludeItemTypes.Add(typeof(LiveTvProgram).Name);
-
if (folderIds.Count == 0)
{
return new QueryResult<BaseItem>();
@@ -1629,221 +1435,61 @@ namespace Emby.Server.Implementations.LiveTv
}
}
+ var limit = query.Limit;
+
if ((query.IsInProgress ?? false))
{
- // TODO: filter
- var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
- var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+ limit = (query.Limit ?? 10) * 2;
+ limit = null;
- return new QueryResult<BaseItem>
- {
- Items = items,
- TotalRecordCount = items.Length
- };
+ //var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
+ //var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+
+ //return new QueryResult<BaseItem>
+ //{
+ // Items = items,
+ // TotalRecordCount = items.Length
+ //};
+
+ dtoOptions.Fields = dtoOptions.Fields.Concat(new[] { ItemFields.Tags }).Distinct().ToArray();
}
- return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+ var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
MediaTypes = new[] { MediaType.Video },
Recursive = true,
- AncestorIds = folderIds.Select(i => i.ToString("N")).ToArray(folderIds.Count),
+ AncestorIds = folderIds.ToArray(folderIds.Count),
IsFolder = false,
IsVirtualItem = false,
- Limit = query.Limit,
+ Limit = limit,
StartIndex = query.StartIndex,
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
EnableTotalRecordCount = query.EnableTotalRecordCount,
IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
Genres = genres.ToArray(genres.Count),
DtoOptions = dtoOptions
});
- }
-
- public QueryResult<BaseItemDto> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- if (user != null && !IsLiveTvEnabled(user))
- {
- return new QueryResult<BaseItemDto>();
- }
-
- if (user == null || (query.IsInProgress ?? false))
- {
- return new QueryResult<BaseItemDto>();
- }
-
- var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
- .SelectMany(i => i.Locations)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
- .ToList();
-
- if (folders.Count == 0)
- {
- return new QueryResult<BaseItemDto>();
- }
-
- var includeItemTypes = new List<string>();
- var excludeItemTypes = new List<string>();
-
- includeItemTypes.Add(typeof(Series).Name);
-
- RemoveFields(options);
-
- var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
- {
- Recursive = true,
- AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(folders.Count),
- Limit = query.Limit,
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
- EnableTotalRecordCount = query.EnableTotalRecordCount,
- IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
- ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
- DtoOptions = options
- });
-
- var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
-
- return new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = internalResult.TotalRecordCount
- };
- }
-
- public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- if (user != null && !IsLiveTvEnabled(user))
- {
- return new QueryResult<BaseItem>();
- }
-
- var folder = GetInternalLiveTvFolder(cancellationToken);
-
- // TODO: Figure out how to merge emby recordings + service recordings
- if (_services.Length == 1)
- {
- return GetEmbyRecordings(query, options, folder.Id, user);
- }
-
- return await GetInternalRecordingsFromServices(query, user, options, folder.Id, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task<QueryResult<BaseItem>> GetInternalRecordingsFromServices(RecordingQuery query, User user, DtoOptions options, Guid internalLiveTvFolderId, CancellationToken cancellationToken)
- {
- await RefreshRecordings(internalLiveTvFolderId, cancellationToken).ConfigureAwait(false);
-
- var internalQuery = new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name },
- DtoOptions = options
- };
-
- if (!string.IsNullOrEmpty(query.ChannelId))
- {
- internalQuery.ChannelIds = new[] { query.ChannelId };
- }
-
- var queryResult = _libraryManager.GetItemList(internalQuery);
- IEnumerable<ILiveTvRecording> recordings = queryResult.Cast<ILiveTvRecording>();
-
- if (!string.IsNullOrWhiteSpace(query.Id))
- {
- var guid = new Guid(query.Id);
-
- recordings = recordings
- .Where(i => i.Id == guid);
- }
-
- if (!string.IsNullOrWhiteSpace(query.GroupId))
- {
- var guid = new Guid(query.GroupId);
-
- recordings = recordings.Where(i => GetRecordingGroupIds(i).Contains(guid));
- }
-
- if (query.IsInProgress.HasValue)
- {
- var val = query.IsInProgress.Value;
- recordings = recordings.Where(i => i.Status == RecordingStatus.InProgress == val);
- }
-
- if (query.Status.HasValue)
- {
- var val = query.Status.Value;
- recordings = recordings.Where(i => i.Status == val);
- }
-
- if (query.IsMovie.HasValue)
- {
- var val = query.IsMovie.Value;
- recordings = recordings.Where(i => i.IsMovie == val);
- }
-
- if (query.IsNews.HasValue)
- {
- var val = query.IsNews.Value;
- recordings = recordings.Where(i => i.IsNews == val);
- }
-
- if (query.IsSeries.HasValue)
- {
- var val = query.IsSeries.Value;
- recordings = recordings.Where(i => i.IsSeries == val);
- }
-
- if (query.IsKids.HasValue)
- {
- var val = query.IsKids.Value;
- recordings = recordings.Where(i => i.IsKids == val);
- }
-
- if (query.IsSports.HasValue)
- {
- var val = query.IsSports.Value;
- recordings = recordings.Where(i => i.IsSports == val);
- }
-
- if (!string.IsNullOrEmpty(query.SeriesTimerId))
- {
- var guid = new Guid(query.SeriesTimerId);
-
- recordings = recordings
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.SeriesTimerId) == guid);
- }
-
- recordings = recordings.OrderByDescending(i => i.StartDate);
- var entityList = recordings.ToList();
- IEnumerable<ILiveTvRecording> entities = entityList;
-
- if (query.StartIndex.HasValue)
+ if ((query.IsInProgress ?? false))
{
- entities = entities.Skip(query.StartIndex.Value);
- }
+ result.Items = result
+ .Items
+ .OfType<Video>()
+ .Where(i => !i.IsCompleteMedia)
+ .ToArray();
- if (query.Limit.HasValue)
- {
- entities = entities.Take(query.Limit.Value);
+ result.TotalRecordCount = result.Items.Length;
}
- return new QueryResult<BaseItem>
- {
- Items = entities.Cast<BaseItem>().ToArray(),
- TotalRecordCount = entityList.Count
- };
+ return result;
}
- public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null)
+ public Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null)
{
- var programTuples = new List<Tuple<BaseItemDto, string, string, string>>();
+ var programTuples = new List<Tuple<BaseItemDto, 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)
{
@@ -1888,7 +1534,7 @@ namespace Emby.Server.Implementations.LiveTv
if (hasChannelInfo || hasChannelImage)
{
- var channel = GetInternalChannel(program.ChannelId);
+ var channel = _libraryManager.GetItemById(program.ChannelId) as LiveTvChannel;
if (channel != null)
{
@@ -1903,19 +1549,12 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- var serviceName = program.ServiceName;
-
- if (hasServiceName)
- {
- dto.ServiceName = serviceName;
- }
-
var externalSeriesId = program.ExternalSeriesId;
- programTuples.Add(new Tuple<BaseItemDto, string, string, string>(dto, serviceName, GetItemExternalId(program), externalSeriesId));
+ programTuples.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, externalSeriesId));
}
- await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
+ return AddRecordingInfo(programTuples, CancellationToken.None);
}
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
@@ -1923,73 +1562,21 @@ namespace Emby.Server.Implementations.LiveTv
return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
}
- public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null)
- {
- var recording = (ILiveTvRecording)item;
- var service = GetService(recording);
-
- var channel = string.IsNullOrWhiteSpace(recording.ChannelId) ? null : GetInternalChannel(recording.ChannelId);
-
- var info = recording;
-
- dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) || service == null
- ? null
- : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
-
- dto.TimerId = string.IsNullOrEmpty(info.TimerId) || service == null
- ? null
- : _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
-
- dto.StartDate = info.StartDate;
- dto.Status = info.Status.ToString();
- dto.IsRepeat = info.IsRepeat;
- dto.EpisodeTitle = info.EpisodeTitle;
- dto.IsMovie = info.IsMovie;
- dto.IsSeries = info.IsSeries;
- dto.IsSports = info.IsSports;
- dto.IsLive = info.IsLive;
- dto.IsNews = info.IsNews;
- dto.IsKids = info.IsKids;
- dto.IsPremiere = info.IsPremiere;
-
- if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
- {
- var now = DateTime.UtcNow.Ticks;
- var start = info.StartDate.Ticks;
- var end = info.EndDate.Value.Ticks;
-
- var pct = now - start;
- pct /= end;
- pct *= 100;
- dto.CompletionPercentage = pct;
- }
-
- if (channel != null)
- {
- dto.ChannelName = channel.Name;
-
- if (channel.HasImage(ImageType.Primary))
- {
- dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
- }
- }
- }
-
public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
{
var service = EmbyTV.EmbyTV.Current;
var info = activeRecordingInfo.Timer;
- var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
+ var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
? null
- : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
+ : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N");
dto.TimerId = string.IsNullOrEmpty(info.Id)
? null
- : _tvDtoService.GetInternalTimerId(service.Name, info.Id).ToString("N");
+ : _tvDtoService.GetInternalTimerId(info.Id);
var startDate = info.StartDate;
var endDate = info.EndDate;
@@ -2034,13 +1621,13 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- public async Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options)
{
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
RemoveFields(options);
- var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
+ var internalResult = GetEmbyRecordings(query, options, user);
var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
@@ -2051,7 +1638,7 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
+ private async Task<QueryResult<TimerInfo>> GetTimersInternal(TimerQuery query, CancellationToken cancellationToken)
{
var tasks = _services.Select(async i =>
{
@@ -2104,7 +1691,7 @@ namespace Emby.Server.Implementations.LiveTv
var guid = new Guid(query.SeriesTimerId);
timers = timers
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item2.Name, i.Item1.SeriesTimerId) == guid);
+ .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
}
if (!string.IsNullOrEmpty(query.Id))
@@ -2112,72 +1699,105 @@ namespace Emby.Server.Implementations.LiveTv
var guid = new Guid(query.Id);
timers = timers
- .Where(i => _tvDtoService.GetInternalTimerId(i.Item2.Name, i.Item1.Id) == guid);
+ .Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
}
- var returnList = new List<TimerInfoDto>();
-
- foreach (var i in timers)
- {
- var program = string.IsNullOrEmpty(i.Item1.ProgramId) ?
- null :
- GetInternalProgram(_tvDtoService.GetInternalProgramId(i.Item2.Name, i.Item1.ProgramId).ToString("N"));
-
- var channel = string.IsNullOrEmpty(i.Item1.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
-
- returnList.Add(_tvDtoService.GetTimerInfoDto(i.Item1, i.Item2, program, channel));
- }
-
- var returnArray = returnList
+ var returnArray = timers
+ .Select(i => i.Item1)
.OrderBy(i => i.StartDate)
- .ToArray(returnList.Count);
+ .ToArray();
- return new QueryResult<TimerInfoDto>
+ return new QueryResult<TimerInfo>
{
Items = returnArray,
TotalRecordCount = returnArray.Length
};
}
- public async Task DeleteRecording(string recordingId)
+ public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
{
- var recording = await GetInternalRecording(recordingId, CancellationToken.None).ConfigureAwait(false);
+ var tasks = _services.Select(async i =>
+ {
+ try
+ {
+ var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
+ return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting recordings", ex);
+ return new List<Tuple<TimerInfo, ILiveTvService>>();
+ }
+ });
+ var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ var timers = results.SelectMany(i => i.ToList());
- if (recording == null)
+ if (query.IsActive.HasValue)
{
- throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId));
+ if (query.IsActive.Value)
+ {
+ timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
+ }
+ else
+ {
+ timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
+ }
}
- await DeleteRecording((BaseItem)recording).ConfigureAwait(false);
- }
-
- public async Task DeleteRecording(BaseItem recording)
- {
- var service = GetService(recording.ServiceName);
-
- if (service != null)
+ if (query.IsScheduled.HasValue)
{
- // handle the service being uninstalled and the item hanging around in the database
- try
+ if (query.IsScheduled.Value)
{
- await service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None).ConfigureAwait(false);
+ timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
}
- catch (ResourceNotFoundException)
+ else
{
-
+ timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
}
}
- _lastRecordingRefreshTime = DateTime.MinValue;
+ if (!string.IsNullOrEmpty(query.ChannelId))
+ {
+ var guid = new Guid(query.ChannelId);
+ timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
+ }
+
+ if (!string.IsNullOrEmpty(query.SeriesTimerId))
+ {
+ var guid = new Guid(query.SeriesTimerId);
+
+ timers = timers
+ .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
+ }
+
+ if (!string.IsNullOrEmpty(query.Id))
+ {
+ timers = timers
+ .Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
+ }
+
+ var returnList = new List<TimerInfoDto>();
- // This is the responsibility of the live tv service
- await _libraryManager.DeleteItem((BaseItem)recording, new DeleteOptions
+ foreach (var i in timers)
{
- DeleteFileLocation = false
+ var program = string.IsNullOrEmpty(i.Item1.ProgramId) ?
+ null :
+ _libraryManager.GetItemById(_tvDtoService.GetInternalProgramId(i.Item1.ProgramId)) as LiveTvProgram;
+
+ var channel = string.IsNullOrEmpty(i.Item1.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
- }).ConfigureAwait(false);
+ returnList.Add(_tvDtoService.GetTimerInfoDto(i.Item1, i.Item2, program, channel));
+ }
- _lastRecordingRefreshTime = DateTime.MinValue;
+ var returnArray = returnList
+ .OrderBy(i => i.StartDate)
+ .ToArray(returnList.Count);
+
+ return new QueryResult<TimerInfoDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
}
public async Task CancelTimer(string id)
@@ -2192,15 +1812,17 @@ namespace Emby.Server.Implementations.LiveTv
var service = GetService(timer.ServiceName);
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
- EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
+ if (!(service is EmbyTV.EmbyTV))
{
- Argument = new TimerEventInfo
+ EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
{
- Id = id
- }
- }, _logger);
+ Argument = new TimerEventInfo
+ {
+ Id = id
+ }
+ }, _logger);
+ }
}
public async Task CancelSeriesTimer(string id)
@@ -2215,7 +1837,6 @@ namespace Emby.Server.Implementations.LiveTv
var service = GetService(timer.ServiceName);
await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
EventHelper.FireEventIfNotNull(SeriesTimerCancelled, this, new GenericEventArgs<TimerEventInfo>
{
@@ -2226,18 +1847,6 @@ namespace Emby.Server.Implementations.LiveTv
}, _logger);
}
- public async Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null)
- {
- var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
-
- if (item == null)
- {
- return null;
- }
-
- return _dtoService.GetBaseItemDto((BaseItem)item, options, user);
- }
-
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
var results = await GetTimers(new TimerQuery
@@ -2345,7 +1954,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(i.Item1.ChannelId))
{
var internalChannelId = _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId);
- var channel = GetInternalChannel(internalChannelId);
+ var channel = _libraryManager.GetItemById(internalChannelId);
channelName = channel == null ? null : channel.Name;
}
@@ -2361,11 +1970,17 @@ namespace Emby.Server.Implementations.LiveTv
};
}
+ public BaseItem GetLiveTvChannel(TimerInfo timer, ILiveTvService service)
+ {
+ var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, timer.ChannelId);
+ return _libraryManager.GetItemById(internalChannelId);
+ }
+
public void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> tuples, DtoOptions options, User user)
{
var now = DateTime.UtcNow;
- var channelIds = tuples.Select(i => i.Item2.Id.ToString("N")).Distinct().ToArray();
+ var channelIds = tuples.Select(i => i.Item2.Id).Distinct().ToArray();
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -2374,8 +1989,8 @@ namespace Emby.Server.Implementations.LiveTv
MaxStartDate = now,
MinEndDate = now,
Limit = channelIds.Length,
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
- TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id.ToString("N") },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
+ TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
DtoOptions = options
}) : new List<BaseItem>();
@@ -2383,10 +1998,9 @@ namespace Emby.Server.Implementations.LiveTv
RemoveFields(options);
var currentProgramsList = new List<BaseItem>();
- var currentChannelsDict = new Dictionary<string, BaseItemDto>();
+ var currentChannelsDict = new Dictionary<Guid, BaseItemDto>();
var addCurrentProgram = options.AddCurrentProgram;
- var addServiceName = options.Fields.Contains(ItemFields.ServiceName);
foreach (var tuple in tuples)
{
@@ -2397,17 +2011,11 @@ namespace Emby.Server.Implementations.LiveTv
dto.ChannelNumber = channel.Number;
dto.ChannelType = channel.ChannelType;
- if (addServiceName)
- {
- dto.ServiceName = channel.ServiceName;
- }
-
currentChannelsDict[dto.Id] = dto;
if (addCurrentProgram)
{
- var channelIdString = channel.Id.ToString("N");
- var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString));
+ var currentProgram = programs.FirstOrDefault(i => channel.Id.Equals(i.ChannelId));
if (currentProgram != null)
{
@@ -2422,13 +2030,10 @@ namespace Emby.Server.Implementations.LiveTv
foreach (var programDto in currentProgramDtos)
{
- if (!string.IsNullOrWhiteSpace(programDto.ChannelId))
+ BaseItemDto channelDto;
+ if (currentChannelsDict.TryGetValue(programDto.ChannelId, out channelDto))
{
- BaseItemDto channelDto;
- if (currentChannelsDict.TryGetValue(programDto.ChannelId, out channelDto))
- {
- channelDto.CurrentProgram = programDto;
- }
+ channelDto.CurrentProgram = programDto;
}
}
}
@@ -2436,25 +2041,30 @@ namespace Emby.Server.Implementations.LiveTv
private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
{
- var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ?
+ var service = program != null ?
GetService(program) :
- _services.FirstOrDefault();
+ null;
+
+ if (service == null)
+ {
+ service = _services.First();
+ }
ProgramInfo programInfo = null;
if (program != null)
{
- var channel = GetInternalChannel(program.ChannelId);
+ var channel = _libraryManager.GetItemById(program.ChannelId);
programInfo = new ProgramInfo
{
Audio = program.Audio,
- ChannelId = GetItemExternalId(channel),
+ ChannelId = channel.ExternalId,
CommunityRating = program.CommunityRating,
EndDate = program.EndDate ?? DateTime.MinValue,
EpisodeTitle = program.EpisodeTitle,
- Genres = program.Genres,
- Id = GetItemExternalId(program),
+ Genres = program.Genres.ToList(),
+ Id = program.ExternalId,
IsHD = program.IsHD,
IsKids = program.IsKids,
IsLive = program.IsLive,
@@ -2503,7 +2113,7 @@ namespace Emby.Server.Implementations.LiveTv
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
{
- var program = GetInternalProgram(programId);
+ var program = (LiveTvProgram)_libraryManager.GetItemById(programId);
var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
@@ -2519,8 +2129,8 @@ namespace Emby.Server.Implementations.LiveTv
info.StartDate = program.StartDate;
info.Name = program.Name;
info.Overview = program.Overview;
- info.ProgramId = programDto.Id;
- info.ExternalProgramId = GetItemExternalId(program);
+ info.ProgramId = programDto.Id.ToString("N");
+ info.ExternalProgramId = program.ExternalId;
if (program.EndDate.HasValue)
{
@@ -2545,24 +2155,26 @@ namespace Emby.Server.Implementations.LiveTv
if (supportsNewTimerIds != null)
{
newTimerId = await supportsNewTimerIds.CreateTimer(info, cancellationToken).ConfigureAwait(false);
- newTimerId = _tvDtoService.GetInternalTimerId(timer.ServiceName, newTimerId).ToString("N");
+ newTimerId = _tvDtoService.GetInternalTimerId(newTimerId);
}
else
{
await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
- _lastRecordingRefreshTime = DateTime.MinValue;
_logger.Info("New recording scheduled");
- EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
+ if (!(service is EmbyTV.EmbyTV))
{
- Argument = new TimerEventInfo
+ EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
{
- ProgramId = _tvDtoService.GetInternalProgramId(timer.ServiceName, info.ProgramId).ToString("N"),
- Id = newTimerId
- }
- }, _logger);
+ Argument = new TimerEventInfo
+ {
+ ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
+ Id = newTimerId
+ }
+ }, _logger);
+ }
}
public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
@@ -2588,20 +2200,18 @@ namespace Emby.Server.Implementations.LiveTv
if (supportsNewTimerIds != null)
{
newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
- newTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.ServiceName, newTimerId).ToString("N");
+ newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N");
}
else
{
await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
- _lastRecordingRefreshTime = DateTime.MinValue;
-
EventHelper.FireEventIfNotNull(SeriesTimerCreated, this, new GenericEventArgs<TimerEventInfo>
{
Argument = new TimerEventInfo
{
- ProgramId = _tvDtoService.GetInternalProgramId(timer.ServiceName, info.ProgramId).ToString("N"),
+ ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
Id = newTimerId
}
}, _logger);
@@ -2614,7 +2224,6 @@ namespace Emby.Server.Implementations.LiveTv
var service = GetService(timer.ServiceName);
await service.UpdateTimerAsync(info, cancellationToken).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
}
public async Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
@@ -2624,138 +2233,6 @@ namespace Emby.Server.Implementations.LiveTv
var service = GetService(timer.ServiceName);
await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
- }
-
- private IEnumerable<string> GetRecordingGroupNames(ILiveTvRecording recording)
- {
- var list = new List<string>();
-
- if (recording.IsSeries)
- {
- list.Add(recording.Name);
- }
-
- if (recording.IsKids)
- {
- list.Add("Kids");
- }
-
- if (recording.IsMovie)
- {
- list.Add("Movies");
- }
-
- if (recording.IsNews)
- {
- list.Add("News");
- }
-
- if (recording.IsSports)
- {
- list.Add("Sports");
- }
-
- if (!recording.IsSports && !recording.IsNews && !recording.IsMovie && !recording.IsKids && !recording.IsSeries)
- {
- list.Add("Others");
- }
-
- return list;
- }
-
- private List<Guid> GetRecordingGroupIds(ILiveTvRecording recording)
- {
- return GetRecordingGroupNames(recording).Select(i => i.ToLower()
- .GetMD5())
- .ToList();
- }
-
- public async Task<QueryResult<BaseItemDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken)
- {
- var recordingResult = await GetInternalRecordings(new RecordingQuery
- {
- UserId = query.UserId
-
- }, new DtoOptions(), cancellationToken).ConfigureAwait(false);
-
- var embyServiceName = EmbyTV.EmbyTV.Current.Name;
- var recordings = recordingResult.Items.Where(i => !string.Equals(i.ServiceName, embyServiceName, StringComparison.OrdinalIgnoreCase)).OfType<ILiveTvRecording>().ToList();
-
- var groups = new List<BaseItemDto>();
-
- var series = recordings
- .Where(i => i.IsSeries)
- .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase);
-
- groups.AddRange(series.OrderByString(i => i.Key).Select(i => new BaseItemDto
- {
- Name = i.Key,
- RecordingCount = i.Count()
- }));
-
- groups.Add(new BaseItemDto
- {
- Name = "Kids",
- RecordingCount = recordings.Count(i => i.IsKids)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Movies",
- RecordingCount = recordings.Count(i => i.IsMovie)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "News",
- RecordingCount = recordings.Count(i => i.IsNews)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Sports",
- RecordingCount = recordings.Count(i => i.IsSports)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Others",
- RecordingCount = recordings.Count(i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries)
- });
-
- groups = groups
- .Where(i => i.RecordingCount > 0)
- .ToList();
-
- foreach (var group in groups)
- {
- group.Id = group.Name.ToLower().GetMD5().ToString("N");
- }
-
- return new QueryResult<BaseItemDto>
- {
- Items = groups.ToArray(groups.Count),
- TotalRecordCount = groups.Count
- };
- }
-
- public async Task CloseLiveStream(string id)
- {
- var parts = id.Split(new[] { '_' }, 2);
-
- var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase));
-
- if (service == null)
- {
- throw new ArgumentException("Service not found.");
- }
-
- id = parts[1];
-
- _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
-
- await service.CloseLiveStream(id, CancellationToken.None).ConfigureAwait(false);
}
public GuideInfo GetGuideInfo()
@@ -2776,7 +2253,6 @@ namespace Emby.Server.Implementations.LiveTv
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
private bool _isDisposed = false;
@@ -2792,66 +2268,22 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private async Task<LiveTvServiceInfo[]> GetServiceInfos(CancellationToken cancellationToken)
+ private LiveTvServiceInfo[] GetServiceInfos()
{
- var tasks = Services.Select(i => GetServiceInfo(i, cancellationToken));
-
- return await Task.WhenAll(tasks).ConfigureAwait(false);
+ return Services.Select(GetServiceInfo).ToArray();
}
- private async Task<LiveTvServiceInfo> GetServiceInfo(ILiveTvService service, CancellationToken cancellationToken)
+ private LiveTvServiceInfo GetServiceInfo(ILiveTvService service)
{
- var info = new LiveTvServiceInfo
+ return new LiveTvServiceInfo
{
Name = service.Name
};
-
- var tunerIdPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- try
- {
- var statusInfo = await service.GetStatusInfoAsync(cancellationToken).ConfigureAwait(false);
-
- info.Status = statusInfo.Status;
- info.StatusMessage = statusInfo.StatusMessage;
- info.Version = statusInfo.Version;
- info.HasUpdateAvailable = statusInfo.HasUpdateAvailable;
- info.HomePageUrl = service.HomePageUrl;
- info.IsVisible = statusInfo.IsVisible;
-
- info.Tuners = statusInfo.Tuners.Select(i =>
- {
- string channelName = null;
-
- if (!string.IsNullOrEmpty(i.ChannelId))
- {
- var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId);
- var channel = GetInternalChannel(internalChannelId);
- channelName = channel == null ? null : channel.Name;
- }
-
- var dto = _tvDtoService.GetTunerInfoDto(service.Name, i, channelName);
-
- dto.Id = tunerIdPrefix + dto.Id;
-
- return dto;
-
- }).ToArray();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting service status info from {0}", ex, service.Name ?? string.Empty);
-
- info.Status = LiveTvServiceStatus.Unavailable;
- info.StatusMessage = ex.Message;
- }
-
- return info;
}
- public async Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken)
+ public LiveTvInfo GetLiveTvInfo(CancellationToken cancellationToken)
{
- var services = await GetServiceInfos(CancellationToken.None).ConfigureAwait(false);
+ var services = GetServiceInfos();
var info = new LiveTvInfo
{
@@ -2898,15 +2330,6 @@ namespace Emby.Server.Implementations.LiveTv
return service.ResetTuner(parts[1], cancellationToken);
}
- public BaseItemDto GetLiveTvFolder(string userId, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(userId);
-
- var folder = GetInternalLiveTvFolder(cancellationToken);
-
- return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
- }
-
private void RemoveFields(DtoOptions options)
{
var fields = options.Fields.ToList();
@@ -2921,7 +2344,7 @@ namespace Emby.Server.Implementations.LiveTv
public Folder GetInternalLiveTvFolder(CancellationToken cancellationToken)
{
var name = _localization.GetLocalizedString("HeaderLiveTV");
- return _libraryManager.GetNamedView(name, CollectionType.LiveTv, name, cancellationToken);
+ return _libraryManager.GetNamedView(name, CollectionType.LiveTv, name);
}
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
@@ -2990,7 +2413,6 @@ namespace Emby.Server.Implementations.LiveTv
info.Id = Guid.NewGuid().ToString("N");
list.Add(info);
config.ListingProviders = list.ToArray(list.Count);
- info.EnableNewProgramIds = true;
}
else
{
@@ -3146,9 +2568,38 @@ namespace Emby.Server.Implementations.LiveTv
return _tvDtoService.GetInternalChannelId(serviceName, externalId);
}
- public Guid GetInternalProgramId(string serviceName, string externalId)
+ public Guid GetInternalProgramId(string externalId)
+ {
+ return _tvDtoService.GetInternalProgramId(externalId);
+ }
+
+ public List<BaseItem> GetRecordingFolders(User user)
+ {
+ return GetRecordingFolders(user, false);
+ }
+
+ private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
{
- return _tvDtoService.GetInternalProgramId(serviceName, externalId);
+ var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
+ .SelectMany(i => i.Locations)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Select(i => _libraryManager.FindByPath(i, true))
+ .Where(i => i != null)
+ .Where(i => i.IsVisibleStandalone(user))
+ .SelectMany(i => _libraryManager.GetCollectionFolders(i))
+ .DistinctBy(i => i.Id)
+ .OrderBy(i => i.SortName)
+ .ToList();
+
+ folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
+ {
+ UserId = user.Id,
+ IsRecordingsFolder = true,
+ RefreshLatestChannelItems = refreshChannels
+
+ }).Items);
+
+ return folders.Cast<BaseItem>().ToList();
}
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 29b7c41ef..808672d46 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -15,6 +15,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.LiveTv
{
@@ -26,8 +27,9 @@ namespace Emby.Server.Implementations.LiveTv
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationHost _appHost;
+ private IApplicationPaths _appPaths;
- public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost)
+ public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost)
{
_liveTvManager = liveTvManager;
_jsonSerializer = jsonSerializer;
@@ -35,9 +37,10 @@ namespace Emby.Server.Implementations.LiveTv
_mediaEncoder = mediaEncoder;
_appHost = appHost;
_logger = logManager.GetLogger(GetType().Name);
+ _appPaths = appPaths;
}
- public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
+ public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
{
var baseItem = (BaseItem)item;
@@ -45,20 +48,20 @@ namespace Emby.Server.Implementations.LiveTv
{
var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path);
- if (string.IsNullOrWhiteSpace(baseItem.Path) || activeRecordingInfo != null)
+ if (string.IsNullOrEmpty(baseItem.Path) || activeRecordingInfo != null)
{
return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken);
}
}
- return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
+ return Task.FromResult<IEnumerable<MediaSourceInfo>>(Array.Empty<MediaSourceInfo>());
}
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
private const char StreamIdDelimeter = '_';
private const string StreamIdDelimeterString = "_";
- private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
+ private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
{
IEnumerable<MediaSourceInfo> sources;
@@ -66,30 +69,20 @@ namespace Emby.Server.Implementations.LiveTv
try
{
- if (item is ILiveTvRecording)
+ if (activeRecordingInfo != null)
{
- sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
+ sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken)
.ConfigureAwait(false);
}
else
{
- if (activeRecordingInfo != null)
- {
- sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken)
- .ConfigureAwait(false);
- }
- else
- {
- sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
- .ConfigureAwait(false);
- }
+ sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
+ .ConfigureAwait(false);
}
}
catch (NotImplementedException)
{
- var hasMediaSources = (IHasMediaSources)item;
-
- sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false);
+ sources = _mediaSourceManager.GetStaticMediaSources(item, false);
forceRequireOpening = true;
}
@@ -128,102 +121,15 @@ namespace Emby.Server.Implementations.LiveTv
return list;
}
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken)
+ public async Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- MediaSourceInfo stream = null;
- const bool isAudio = false;
-
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
- IDirectStreamProvider directStreamProvider = null;
-
- if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
- {
- var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, cancellationToken).ConfigureAwait(false);
- stream = info.Item1;
- directStreamProvider = info.Item2;
-
- //allowLiveStreamProbe = false;
- }
- else
- {
- stream = await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false);
- }
-
- try
- {
- if (!allowLiveStreamProbe || !stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1))
- {
- AddMediaInfo(stream, isAudio, cancellationToken);
- }
- else
- {
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error probing live tv stream", ex);
- }
-
- _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(stream));
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(stream, directStreamProvider);
- }
-
- private void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
- {
- mediaSource.DefaultSubtitleStreamIndex = null;
- // Null this out so that it will be treated like a live stream
- mediaSource.RunTimeTicks = null;
+ var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
+ var liveStream = info.Item2;
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
-
- if (audioStream == null || audioStream.Index == -1)
- {
- mediaSource.DefaultAudioStreamIndex = null;
- }
- else
- {
- mediaSource.DefaultAudioStreamIndex = audioStream.Index;
- }
-
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
- if (videoStream != null)
- {
- if (!videoStream.BitRate.HasValue)
- {
- var width = videoStream.Width ?? 1920;
-
- if (width >= 3000)
- {
- videoStream.BitRate = 30000000;
- }
-
- else if (width >= 1900)
- {
- videoStream.BitRate = 20000000;
- }
-
- else if (width >= 1200)
- {
- videoStream.BitRate = 8000000;
- }
-
- else if (width >= 700)
- {
- videoStream.BitRate = 2000000;
- }
- }
- }
-
- // Try to estimate this
- mediaSource.InferTotalBitrate();
- }
-
- public Task CloseMediaSource(string liveStreamId)
- {
- return _liveTvManager.CloseLiveStream(liveStreamId);
+ return liveStream;
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs b/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
deleted file mode 100644
index 992badbb5..000000000
--- a/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Emby.Server.Implementations.LiveTv
-{
- public class RecordingImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
- {
- private readonly ILiveTvManager _liveTvManager;
-
- public RecordingImageProvider(ILiveTvManager liveTvManager)
- {
- _liveTvManager = liveTvManager;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
- {
- return new[] { ImageType.Primary };
- }
-
- public async Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
- {
- var liveTvItem = (ILiveTvRecording)item;
-
- var imageResponse = new DynamicImageResponse();
-
- var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
-
- if (service != null)
- {
- try
- {
- var response = await service.GetRecordingImageAsync(liveTvItem.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (response != null)
- {
- imageResponse.HasImage = true;
- imageResponse.Stream = response.Stream;
- imageResponse.Format = response.Format;
- }
- }
- catch (NotImplementedException)
- {
- }
- }
-
- return imageResponse;
- }
-
- public string Name
- {
- get { return "Live TV Service Provider"; }
- }
-
- public bool Supports(IHasMetadata item)
- {
- return item is ILiveTvRecording;
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- var liveTvItem = item as ILiveTvRecording;
-
- if (liveTvItem != null)
- {
- return !liveTvItem.HasImage(ImageType.Primary);
- }
- return false;
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 45e96c36d..ca5e51971 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
@@ -55,22 +56,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
ChannelCache cache = null;
var key = tuner.Id;
- if (enableCache && !string.IsNullOrWhiteSpace(key) && _channelCache.TryGetValue(key, out cache))
+ if (enableCache && !string.IsNullOrEmpty(key) && _channelCache.TryGetValue(key, out cache))
{
- if (DateTime.UtcNow - cache.Date < TimeSpan.FromMinutes(60))
- {
- return cache.Channels.ToList();
- }
+ return cache.Channels.ToList();
}
var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
var list = result.ToList();
- Logger.Info("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
+ //Logger.Info("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
- if (!string.IsNullOrWhiteSpace(key) && list.Count > 0)
+ if (!string.IsNullOrEmpty(key) && list.Count > 0)
{
cache = cache ?? new ChannelCache();
- cache.Date = DateTime.UtcNow;
cache.Channels = list;
_channelCache.AddOrUpdate(key, cache, (k, v) => cache);
}
@@ -137,11 +134,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return list;
}
- protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
+ protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, ChannelInfo channel, CancellationToken cancellationToken);
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(channelId))
+ if (string.IsNullOrEmpty(channelId))
{
throw new ArgumentNullException("channelId");
}
@@ -150,17 +147,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var hosts = GetTunerHosts();
- var hostsWithChannel = new List<TunerHostInfo>();
-
foreach (var host in hosts)
{
try
{
var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+ var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
- if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
+ if (channelInfo != null)
{
- hostsWithChannel.Add(host);
+ return await GetChannelStreamMediaSources(host, channelInfo, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -168,44 +164,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Logger.Error("Error getting channels", ex);
}
}
-
- foreach (var host in hostsWithChannel)
- {
- try
- {
- // Check to make sure the tuner is available
- // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error
- if (hostsWithChannel.Count > 1 && !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
- {
- Logger.Error("Tuner is not currently available");
- continue;
- }
-
- var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false);
-
- // Prefix the id with the host Id so that we can easily find it
- foreach (var mediaSource in mediaSources)
- {
- mediaSource.Id = host.Id + mediaSource.Id;
- }
-
- return mediaSources;
- }
- catch (Exception ex)
- {
- Logger.Error("Error opening tuner", ex);
- }
- }
}
return new List<MediaSourceInfo>();
}
- protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
+ protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tuner, ChannelInfo channel, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
- public async Task<ILiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+ public async Task<ILiveStream> GetChannelStream(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(channelId))
+ if (string.IsNullOrEmpty(channelId))
{
throw new ArgumentNullException("channelId");
}
@@ -217,44 +185,34 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var hosts = GetTunerHosts();
- var hostsWithChannel = new List<TunerHostInfo>();
+ var hostsWithChannel = new List<Tuple<TunerHostInfo, ChannelInfo>>();
foreach (var host in hosts)
{
- if (string.IsNullOrWhiteSpace(streamId))
+ try
{
- try
- {
- var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+ var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
- if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
- {
- hostsWithChannel.Add(host);
- }
- }
- catch (Exception ex)
+ if (channelInfo != null)
{
- Logger.Error("Error getting channels", ex);
+ hostsWithChannel.Add(new Tuple<TunerHostInfo, ChannelInfo>(host, channelInfo));
}
}
- else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
+ catch (Exception ex)
{
- hostsWithChannel = new List<TunerHostInfo> { host };
- streamId = streamId.Substring(host.Id.Length);
- break;
+ Logger.Error("Error getting channels", ex);
}
}
- foreach (var host in hostsWithChannel)
+ foreach (var hostTuple in hostsWithChannel)
{
- if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
+ var host = hostTuple.Item1;
+ var channelInfo = hostTuple.Item2;
try
{
- var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
+ var liveStream = await GetChannelStream(host, channelInfo, streamId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
var startTime = DateTime.UtcNow;
await liveStream.Open(cancellationToken).ConfigureAwait(false);
var endTime = DateTime.UtcNow;
@@ -270,21 +228,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
throw new LiveTvConflictException();
}
- protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- try
- {
- return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error checking tuner availability", ex);
- return false;
- }
- }
-
- protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
-
protected virtual string ChannelIdPrefix
{
get
@@ -294,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
protected virtual bool IsValidChannelId(string channelId)
{
- if (string.IsNullOrWhiteSpace(channelId))
+ if (string.IsNullOrEmpty(channelId))
{
throw new ArgumentNullException("channelId");
}
@@ -309,7 +252,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private class ChannelCache
{
- public DateTime Date;
public List<ChannelInfo> Channels;
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 74758e906..e873eb8e9 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -21,6 +21,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
@@ -68,11 +69,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var id = ChannelIdPrefix + i.GuideNumber;
- if (!info.EnableNewHdhrChannelIds)
- {
- id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N");
- }
-
return id;
}
@@ -90,7 +86,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
using (var stream = response.Content)
{
- var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
+ var lineup = await JsonSerializer.DeserializeFromStreamAsync<List<Channels>>(stream).ConfigureAwait(false) ?? new List<Channels>();
if (info.ImportFavoritesOnly)
{
@@ -131,12 +127,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
{
+ var cacheKey = info.Id;
+
lock (_modelCache)
{
- DiscoverResponse response;
- if (_modelCache.TryGetValue(info.Url, out response))
+ if (!string.IsNullOrEmpty(cacheKey))
{
- if ((DateTime.UtcNow - response.DateQueried).TotalHours <= 12)
+ DiscoverResponse response;
+ if (_modelCache.TryGetValue(cacheKey, out response))
{
return response;
}
@@ -149,20 +147,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/discover.json", GetApiUrl(info)),
CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
+ TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds),
BufferContent = false
}, "GET").ConfigureAwait(false))
{
using (var stream = response.Content)
{
- var discoverResponse = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
+ var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync<DiscoverResponse>(stream).ConfigureAwait(false);
- if (!string.IsNullOrWhiteSpace(info.Id))
+ if (!string.IsNullOrEmpty(cacheKey))
{
lock (_modelCache)
{
- _modelCache[info.Id] = discoverResponse;
+ _modelCache[cacheKey] = discoverResponse;
}
}
@@ -179,12 +177,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
ModelNumber = defaultValue
};
- if (!string.IsNullOrWhiteSpace(info.Id))
+ if (!string.IsNullOrEmpty(cacheKey))
{
// HDHR4 doesn't have this api
lock (_modelCache)
{
- _modelCache[info.Id] = response;
+ _modelCache[cacheKey] = response;
}
}
return response;
@@ -395,10 +393,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
videoCodec = "h264";
videoBitrate = 15000000;
}
+ else if (string.Equals(profile, "internet720", StringComparison.OrdinalIgnoreCase))
+ {
+ width = 1280;
+ height = 720;
+ isInterlaced = false;
+ videoCodec = "h264";
+ videoBitrate = 8000000;
+ }
else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase))
{
width = 960;
- height = 546;
+ height = 540;
isInterlaced = false;
videoCodec = "h264";
videoBitrate = 2500000;
@@ -511,8 +517,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
SupportsTranscoding = true,
IsInfiniteStream = true,
IgnoreDts = true,
- SupportsProbing = false,
- //AnalyzeDurationMs = 2000000
//IgnoreIndex = true,
//ReadAtNativeFramerate = true
};
@@ -522,19 +526,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return mediaSource;
}
- protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+ protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken)
{
var list = new List<MediaSourceInfo>();
- if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- return list;
- }
+ var channelId = channelInfo.Id;
var hdhrId = GetHdHrIdFromChannelId(channelId);
- var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false);
- var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
-
var hdHomerunChannelInfo = channelInfo as HdHomerunChannelInfo;
var isLegacyTuner = hdHomerunChannelInfo != null && hdHomerunChannelInfo.IsLegacyTuner;
@@ -548,12 +546,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- var model = modelInfo == null ? string.Empty : (modelInfo.ModelNumber ?? string.Empty);
- if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+ if (modelInfo != null && modelInfo.SupportsTranscoding)
{
- list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
-
if (info.AllowHWTranscoding)
{
list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy"));
@@ -564,6 +559,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240"));
list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile"));
}
+
+ list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
}
}
catch
@@ -580,31 +577,31 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return list;
}
- protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
var profile = streamId.Split('_')[0];
- Logger.Info("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelId, streamId, profile);
-
- var hdhrId = GetHdHrIdFromChannelId(channelId);
+ Logger.Info("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);
- var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false);
- var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
+ var hdhrId = GetHdHrIdFromChannelId(channelInfo.Id);
var hdhomerunChannel = channelInfo as HdHomerunChannelInfo;
- var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile);
var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
+ if (!modelInfo.SupportsTranscoding)
+ {
+ profile = "native";
+ }
+
+ var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile);
+
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
{
- return new HdHomerunUdpStream(mediaSource, info, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
+ return new HdHomerunUdpStream(mediaSource, info, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
}
- // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet
- var enableHttpStream = _environment.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX
- || _environment.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.BSD;
- enableHttpStream = true;
+ var enableHttpStream = true;
if (enableHttpStream)
{
mediaSource.Protocol = MediaProtocol.Http;
@@ -618,10 +615,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
mediaSource.Path = httpUrl;
- return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
+ return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
}
- return new HdHomerunUdpStream(mediaSource, info, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
+ return new HdHomerunUdpStream(mediaSource, info, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
}
public async Task Validate(TunerHostInfo info)
@@ -649,13 +646,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false);
-
- return info.Any(i => i.Status == LiveTvTunerStatus.Available);
- }
-
public class DiscoverResponse
{
public string FriendlyName { get; set; }
@@ -668,16 +658,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public string LineupURL { get; set; }
public int TunerCount { get; set; }
- public DateTime DateQueried { get; set; }
-
- public DiscoverResponse()
+ public bool SupportsTranscoding
{
- DateQueried = DateTime.UtcNow;
+ get
+ {
+ var model = ModelNumber ?? string.Empty;
+
+ if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+ {
+ return true;
+ }
+
+ return false;
+ }
}
}
public async Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken)
{
+ lock (_modelCache)
+ {
+ _modelCache.Clear();
+ }
+
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token;
var list = new List<TunerHostInfo>();
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 5156f1744..2bc5a0ed3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -7,6 +7,8 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Controller.LiveTv;
+using System.Net;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
@@ -61,7 +63,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!String.IsNullOrEmpty(_channel))
{
- if (!string.IsNullOrWhiteSpace(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
{
commands.Add(Tuple.Create("vchannel", String.Format("{0} transcode={1}", _channel, _profile)));
}
@@ -86,11 +88,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private static ushort GetSetReply = 5;
private uint? _lockkey = null;
- private int _activeTuner = 0;
+ private int _activeTuner = -1;
private readonly ISocketFactory _socketFactory;
private IpAddressInfo _remoteIp;
private ILogger _logger;
+ private ISocket _currentTcpSocket;
public HdHomerunManager(ISocketFactory socketFactory, ILogger logger)
{
@@ -100,10 +103,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public void Dispose()
{
- var task = StopStreaming();
+ using (var socket = _currentTcpSocket)
+ {
+ if (socket != null)
+ {
+ _currentTcpSocket = null;
- Task.WaitAll(task);
- GC.SuppressFinalize(this);
+ var task = StopStreaming(socket);
+ Task.WaitAll(task);
+ }
+ }
}
public async Task<bool> CheckTunerAvailability(IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken)
@@ -130,58 +139,45 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
}
- public async Task StartStreaming(IpAddressInfo remoteIp, IpAddressInfo localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
+ public async Task StartStreaming(IpAddressInfo remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
{
_remoteIp = remoteIp;
-
- using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
- {
- var receiveBuffer = new byte[8192];
-
- if (!_lockkey.HasValue)
- {
- var rand = new Random();
- _lockkey = (uint)rand.Next();
- }
- var lockKeyValue = _lockkey.Value;
+ var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort);
+ _currentTcpSocket = tcpClient;
- var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
+ var receiveBuffer = new byte[8192];
- for (int i = 0; i < numTuners; ++i)
- {
- if (!await CheckTunerAvailability(tcpClient, _remoteIp, i, cancellationToken).ConfigureAwait(false))
- continue;
+ if (!_lockkey.HasValue)
+ {
+ var rand = new Random();
+ _lockkey = (uint)rand.Next();
+ }
- _activeTuner = i;
- var lockKeyString = String.Format("{0:d}", lockKeyValue);
- var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
- await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
- string returnVal;
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
- continue;
+ var lockKeyValue = _lockkey.Value;
- var commandList = commands.GetCommands();
- foreach(Tuple<string,string> command in commandList)
- {
- var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
- await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
- {
- await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
- continue;
- }
+ var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
- }
-
- var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
- var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
+ for (int i = 0; i < numTuners; ++i)
+ {
+ if (!await CheckTunerAvailability(tcpClient, _remoteIp, i, cancellationToken).ConfigureAwait(false))
+ continue;
+
+ _activeTuner = i;
+ var lockKeyString = String.Format("{0:d}", lockKeyValue);
+ var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
+ await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
+ var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
+ string returnVal;
+ // parse response to make sure it worked
+ if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
+ continue;
- await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
+ var commandList = commands.GetCommands();
+ foreach (Tuple<string, string> command in commandList)
+ {
+ var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
+ await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
@@ -190,9 +186,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
continue;
}
- break;
}
+
+ var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
+ var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
+
+ await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
+ response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
+ // parse response to make sure it worked
+ if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
+ {
+ await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
+ continue;
+ }
+
+ return;
}
+
+ _activeTuner = -1;
+ throw new LiveTvConflictException();
}
public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken)
@@ -220,32 +232,31 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- public async Task StopStreaming()
+ public Task StopStreaming(ISocket socket)
{
var lockKey = _lockkey;
if (!lockKey.HasValue)
- return;
+ return Task.CompletedTask;
- using (var socket = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
- {
- await ReleaseLockkey(socket, lockKey.Value).ConfigureAwait(false);
- }
+ return ReleaseLockkey(socket, lockKey.Value);
}
private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue)
{
_logger.Info("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
+ var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
+
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
- await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
+ await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
_lockkey = null;
- await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
+ await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 6e93055be..33103979e 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -9,23 +9,25 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using MediaBrowser.Model.LiveTv;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Net;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
{
private readonly IServerApplicationHost _appHost;
- private readonly ISocketFactory _socketFactory;
+ private readonly MediaBrowser.Model.Net.ISocketFactory _socketFactory;
private readonly IHdHomerunChannelCommands _channelCommands;
private readonly int _numTuners;
private readonly INetworkManager _networkManager;
- public HdHomerunUdpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment)
- : base(mediaSource, tunerHostInfo, environment, fileSystem, logger, appPaths)
+ public HdHomerunUdpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, MediaBrowser.Model.Net.ISocketFactory socketFactory, INetworkManager networkManager)
+ : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
{
_appHost = appHost;
_socketFactory = socketFactory;
@@ -36,6 +38,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
EnableStreamSharing = true;
}
+ private Socket CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
+ {
+ var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
+
+ return socket;
+ }
+
public override async Task Open(CancellationToken openCancellationToken)
{
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
@@ -49,14 +58,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host);
- var remoteAddress = _networkManager.ParseIpAddress(uri.Host);
- IpAddressInfo localAddress = null;
- using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false))
+ var remoteAddress = IPAddress.Parse(uri.Host);
+ var embyRemoteAddress = _networkManager.ParseIpAddress(uri.Host);
+ IPAddress localAddress = null;
+ using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{
try
{
- tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort));
- localAddress = tcpSocket.LocalEndPoint.IpAddress;
+ tcpSocket.Connect(new IPEndPoint(remoteAddress, HdHomerunManager.HdHomeRunPort));
+ localAddress = ((IPEndPoint)tcpSocket.LocalEndPoint).Address;
tcpSocket.Close();
}
catch (Exception)
@@ -72,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
// send url to start streaming
- await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
+ await hdHomerunManager.StartStreaming(embyRemoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -91,14 +101,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var taskCompletionSource = new TaskCompletionSource<bool>();
- StartStreaming(udpClient, hdHomerunManager, remoteAddress, localAddress, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
+ StartStreaming(udpClient, hdHomerunManager, remoteAddress, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
- OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
- OpenedMediaSource.Protocol = MediaProtocol.Http;
+ MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
@@ -107,12 +117,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
await taskCompletionSource.Task.ConfigureAwait(false);
}
- protected override void CloseInternal()
- {
- LiveStreamCancellationTokenSource.Cancel();
- }
-
- private Task StartStreaming(ISocket udpClient, HdHomerunManager hdHomerunManager, IpAddressInfo remoteAddress, IpAddressInfo localAddress, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private Task StartStreaming(MediaBrowser.Model.Net.ISocket udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
{
@@ -136,19 +141,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
EnableStreamSharing = false;
-
- try
- {
- await hdHomerunManager.StopStreaming().ConfigureAwait(false);
- }
- catch
- {
-
- }
}
}
- await DeleteTempFile(TempFilePath).ConfigureAwait(false);
+ await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
});
}
@@ -161,7 +157,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
private static int RtpHeaderBytes = 12;
- private async Task CopyTo(ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
var bufferSize = 81920;
@@ -191,6 +187,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!resolved)
{
resolved = true;
+ DateOpened = DateTime.UtcNow;
Resolve(openTaskCompletionSource);
}
}
@@ -202,10 +199,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
private static int RtpHeaderBytes = 12;
private static int PacketSize = 1316;
- private readonly ISocket _udpClient;
+ private readonly MediaBrowser.Model.Net.ISocket _udpClient;
bool disposed;
- public UdpClientStream(ISocket udpClient) : base()
+ public UdpClientStream(MediaBrowser.Model.Net.ISocket udpClient) : base()
{
_udpClient = udpClient;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index f6758e94e..7e0ac4131 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -11,47 +11,52 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
using MediaBrowser.Model.LiveTv;
+using System.Linq;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class LiveStream : ILiveStream
{
public MediaSourceInfo OriginalMediaSource { get; set; }
- public MediaSourceInfo OpenedMediaSource { get; set; }
- public int ConsumerCount
- {
- get { return SharedStreamIds.Count; }
- }
+ public MediaSourceInfo MediaSource { get; set; }
+
+ public int ConsumerCount { get; set; }
public string OriginalStreamId { get; set; }
public bool EnableStreamSharing { get; set; }
public string UniqueId { get; private set; }
- public List<string> SharedStreamIds { get; private set; }
- protected readonly IEnvironmentInfo Environment;
protected readonly IFileSystem FileSystem;
protected readonly IServerApplicationPaths AppPaths;
- protected string TempFilePath;
+ protected string TempFilePath;
protected readonly ILogger Logger;
protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
public string TunerHostId { get; private set; }
- public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
+ public DateTime DateOpened { get; protected set; }
+
+ public Func<Task> OnClose { get; set; }
+
+ public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
{
OriginalMediaSource = mediaSource;
- Environment = environment;
FileSystem = fileSystem;
- OpenedMediaSource = mediaSource;
+ MediaSource = mediaSource;
Logger = logger;
EnableStreamSharing = true;
- SharedStreamIds = new List<string>();
UniqueId = Guid.NewGuid().ToString("N");
- TunerHostId = tuner.Id;
+
+ if (tuner != null)
+ {
+ TunerHostId = tuner.Id;
+ }
AppPaths = appPaths;
+ ConsumerCount = 1;
SetTempFilePath("ts");
}
@@ -62,20 +67,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public virtual Task Open(CancellationToken openCancellationToken)
{
- return Task.FromResult(true);
+ DateOpened = DateTime.UtcNow;
+ return Task.CompletedTask;
}
- public void Close()
+ public Task Close()
{
EnableStreamSharing = false;
Logger.Info("Closing " + GetType().Name);
- CloseInternal();
+ LiveStreamCancellationTokenSource.Cancel();
+
+ if (OnClose != null)
+ {
+ return CloseWithExternalFn();
+ }
+
+ return Task.CompletedTask;
}
- protected virtual void CloseInternal()
+ private async Task CloseWithExternalFn()
{
+ try
+ {
+ await OnClose().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error closing live stream", ex);
+ }
}
protected Stream GetInputStream(string path, bool allowAsyncFileRead)
@@ -90,91 +111,123 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
}
- protected async Task DeleteTempFile(string path, int retryCount = 0)
+ public Task DeleteTempFiles()
+ {
+ return DeleteTempFiles(GetStreamFilePaths());
+ }
+
+ protected async Task DeleteTempFiles(List<string> paths, int retryCount = 0)
{
if (retryCount == 0)
{
- Logger.Info("Deleting temp file {0}", path);
+ Logger.Info("Deleting temp files {0}", string.Join(", ", paths.ToArray()));
}
- try
- {
- FileSystem.DeleteFile(path);
- return;
- }
- catch (DirectoryNotFoundException)
- {
- return;
- }
- catch (FileNotFoundException)
- {
- return;
- }
- catch
- {
+ var failedFiles = new List<string>();
+ foreach (var path in paths)
+ {
+ try
+ {
+ FileSystem.DeleteFile(path);
+ }
+ catch (DirectoryNotFoundException)
+ {
+ }
+ catch (FileNotFoundException)
+ {
+ }
+ catch (Exception ex)
+ {
+ //Logger.ErrorException("Error deleting file {0}", ex, path);
+ failedFiles.Add(path);
+ }
}
- if (retryCount > 20)
+ if (failedFiles.Count > 0 && retryCount <= 40)
{
- return;
+ await Task.Delay(500).ConfigureAwait(false);
+ await DeleteTempFiles(failedFiles, retryCount + 1).ConfigureAwait(false);
}
+ }
- await Task.Delay(500).ConfigureAwait(false);
- await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false);
+ protected virtual List<string> GetStreamFilePaths()
+ {
+ return new List<string> { TempFilePath };
}
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
- var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
+ var allowAsync = false;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
- using (var inputStream = (FileStream)GetInputStream(TempFilePath, allowAsync))
+ bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
+
+ var nextFileInfo = GetNextFile(null);
+ var nextFile = nextFileInfo.Item1;
+ var isLastFile = nextFileInfo.Item2;
+
+ while (!string.IsNullOrEmpty(nextFile))
{
- TrySeek(inputStream, -20000);
+ var emptyReadLimit = isLastFile ? EmptyReadLimit : 1;
+
+ await CopyFile(nextFile, seekFile, emptyReadLimit, allowAsync, stream, cancellationToken).ConfigureAwait(false);
- await CopyTo(inputStream, stream, 81920, null, cancellationToken).ConfigureAwait(false);
+ seekFile = false;
+ nextFileInfo = GetNextFile(nextFile);
+ nextFile = nextFileInfo.Item1;
+ isLastFile = nextFileInfo.Item2;
}
+
+ Logger.Info("Live Stream ended.");
}
- private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
+ private Tuple<string, bool> GetNextFile(string currentFile)
{
- byte[] buffer = new byte[bufferSize];
+ var files = GetStreamFilePaths();
- var eofCount = 0;
- var emptyReadLimit = 1000;
+ //Logger.Info("Live stream files: {0}", string.Join(", ", files.ToArray()));
- while (eofCount < emptyReadLimit)
+ if (string.IsNullOrEmpty(currentFile))
{
- cancellationToken.ThrowIfCancellationRequested();
+ return new Tuple<string, bool>(files.Last(), true);
+ }
+
+ var nextIndex = files.FindIndex(i => string.Equals(i, currentFile, StringComparison.OrdinalIgnoreCase)) + 1;
+
+ var isLastFile = nextIndex == files.Count - 1;
- var bytesRead = source.Read(buffer, 0, buffer.Length);
+ return new Tuple<string, bool>(files.ElementAtOrDefault(nextIndex), isLastFile);
+ }
+
+ private async Task CopyFile(string path, bool seekFile, int emptyReadLimit, bool allowAsync, Stream stream, CancellationToken cancellationToken)
+ {
+ //Logger.Info("Opening live stream file {0}. Empty read limit: {1}", path, emptyReadLimit);
- if (bytesRead == 0)
+ using (var inputStream = (FileStream)GetInputStream(path, allowAsync))
+ {
+ if (seekFile)
{
- eofCount++;
- await Task.Delay(10, cancellationToken).ConfigureAwait(false);
+ TrySeek(inputStream, -20000);
}
- else
- {
- eofCount = 0;
- //await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
- destination.Write(buffer, 0, bytesRead);
+ await ApplicationHost.StreamHelper.CopyToAsync(inputStream, stream, 81920, emptyReadLimit, cancellationToken).ConfigureAwait(false);
+ }
+ }
- if (onStarted != null)
- {
- onStarted();
- onStarted = null;
- }
- }
+ protected virtual int EmptyReadLimit
+ {
+ get
+ {
+ return 1000;
}
}
private void TrySeek(FileStream stream, long offset)
{
+ //Logger.Info("TrySeek live stream");
try
{
stream.Seek(offset, SeekOrigin.End);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 04c5303f1..a1bff2b5b 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using System.IO;
+using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
@@ -27,13 +28,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private readonly IServerApplicationHost _appHost;
private readonly IEnvironmentInfo _environment;
private readonly INetworkManager _networkManager;
+ private readonly IMediaSourceManager _mediaSourceManager;
- public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+ public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
_environment = environment;
_networkManager = networkManager;
+ _mediaSourceManager = mediaSourceManager;
}
public override string Type
@@ -76,7 +79,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return Task.FromResult(list);
}
- private string[] _disallowedSharedStreamExtensions = new string[]
+ private string[] _disallowedSharedStreamExtensions = new string[]
{
".mkv",
".mp4",
@@ -84,21 +87,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
".mpd"
};
- protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
var tunerCount = info.TunerCount;
if (tunerCount > 0)
{
- var liveStreams = await EmbyTV.EmbyTV.Current.GetLiveStreams(info, cancellationToken).ConfigureAwait(false);
+ var tunerHostId = info.Id;
+ var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase)).ToList();
- if (liveStreams.Count >= info.TunerCount)
+ if (liveStreams.Count >= tunerCount)
{
- throw new LiveTvConflictException();
+ throw new LiveTvConflictException("M3U simultaneous stream limit has been reached.");
}
}
- var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
+ var sources = await GetChannelStreamMediaSources(info, channelInfo, cancellationToken).ConfigureAwait(false);
var mediaSource = sources.First();
@@ -108,11 +112,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
+ return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
}
}
- return new LiveStream(mediaSource, info, _environment, FileSystem, Logger, Config.ApplicationPaths);
+ return new LiveStream(mediaSource, info, FileSystem, Logger, Config.ApplicationPaths);
}
public async Task Validate(TunerHostInfo info)
@@ -123,41 +127,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+ protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken)
{
- var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
- var channel = channels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
- if (channel != null)
- {
- return new List<MediaSourceInfo> { CreateMediaSourceInfo(info, channel) };
- }
- return new List<MediaSourceInfo>();
+ return Task.FromResult(new List<MediaSourceInfo> { CreateMediaSourceInfo(info, channelInfo) });
}
protected virtual MediaSourceInfo CreateMediaSourceInfo(TunerHostInfo info, ChannelInfo channel)
{
var path = channel.Path;
- MediaProtocol protocol = MediaProtocol.File;
- if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Http;
- }
- else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Rtmp;
- }
- else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Rtsp;
- }
- else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Udp;
- }
- else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Rtmp;
- }
+
+ var supportsDirectPlay = !info.EnableStreamLooping && info.TunerCount == 0;
+ var supportsDirectStream = !info.EnableStreamLooping;
+
+ var protocol = _mediaSourceManager.GetPathProtocol(path);
Uri uri;
var isRemote = true;
@@ -166,7 +148,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
isRemote = !_networkManager.IsInLocalNetwork(uri.Host);
}
- var supportsDirectPlay = !info.EnableStreamLooping && info.TunerCount == 0;
+ var httpHeaders = new Dictionary<string, string>();
+
+ if (protocol == MediaProtocol.Http)
+ {
+ // Use user-defined user-agent. If there isn't one, make it look like a browser.
+ httpHeaders["User-Agent"] = string.IsNullOrWhiteSpace(info.UserAgent) ?
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.85 Safari/537.36" :
+ info.UserAgent;
+ }
var mediaSource = new MediaSourceInfo
{
@@ -186,7 +176,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Type = MediaStreamType.Audio,
// Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1
-
}
},
RequiresOpening = true,
@@ -200,7 +189,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
IsRemote = isRemote,
IgnoreDts = true,
- SupportsDirectPlay = supportsDirectPlay
+ SupportsDirectPlay = supportsDirectPlay,
+ SupportsDirectStream = supportsDirectStream,
+
+ RequiredHttpHeaders = httpHeaders
};
mediaSource.InferTotalBitrate();
@@ -208,11 +200,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return mediaSource;
}
- protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
public Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken)
{
return Task.FromResult(new List<TunerHostInfo>());
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index af7491e86..572edb167 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -15,6 +15,7 @@ using MediaBrowser.Model.System;
using System.Globalization;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.LiveTv;
+using System.Collections.Generic;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
@@ -23,8 +24,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- public SharedHttpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
- : base(mediaSource, tunerHostInfo, environment, fileSystem, logger, appPaths)
+ public SharedHttpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
+ : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
{
_httpClient = httpClient;
_appHost = appHost;
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var typeName = GetType().Name;
Logger.Info("Opening " + typeName + " Live stream from {0}", url);
- var response = await _httpClient.SendAsync(new HttpRequestOptions
+ var httpRequestOptions = new HttpRequestOptions
{
Url = url,
CancellationToken = CancellationToken.None,
@@ -58,8 +59,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
LogResponse = true,
LogResponseHeaders = true
+ };
- }, "GET").ConfigureAwait(false);
+ foreach (var header in mediaSource.RequiredHttpHeaders)
+ {
+ httpRequestOptions.RequestHeaders[header.Key] = header.Value;
+ }
+
+ var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false);
var extension = "ts";
var requiresRemux = false;
@@ -98,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
- OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
- OpenedMediaSource.Protocol = MediaProtocol.Http;
+ MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.Path = TempFilePath;
//OpenedMediaSource.Protocol = MediaProtocol.File;
@@ -110,25 +117,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
await taskCompletionSource.Task.ConfigureAwait(false);
-
- if (OpenedMediaSource.SupportsProbing)
- {
- var elapsed = (DateTime.UtcNow - now).TotalMilliseconds;
-
- var delay = Convert.ToInt32(3000 - elapsed);
-
- if (delay > 0)
- {
- Logger.Info("Delaying shared stream by {0}ms to allow the buffer to build.", delay);
-
- await Task.Delay(delay).ConfigureAwait(false);
- }
- }
- }
-
- protected override void CloseInternal()
- {
- LiveStreamCancellationTokenSource.Cancel();
}
private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
@@ -145,7 +133,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
{
- StreamHelper.CopyTo(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
+ await ApplicationHost.StreamHelper.CopyToAsync(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false);
}
}
}
@@ -158,12 +146,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Logger.ErrorException("Error copying live stream.", ex);
}
EnableStreamSharing = false;
- await DeleteTempFile(TempFilePath).ConfigureAwait(false);
+ await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
});
}
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
{
+ DateOpened = DateTime.UtcNow;
openTaskCompletionSource.TrySetResult(true);
}
}
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 54200605d..3a84195ee 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u0627\u0644\u0623\u062d\u062f\u062b",
"ValueSpecialEpisodeName": "\u062e\u0627\u0635 - {0}",
"Inherit": "\u062a\u0648\u0631\u064a\u062b",
@@ -24,6 +32,7 @@
"Channels": "\u0627\u0644\u0642\u0646\u0648\u0627\u062a",
"Movies": "\u0627\u0644\u0623\u0641\u0644\u0627\u0645",
"Albums": "\u0627\u0644\u0623\u0644\u0628\u0648\u0645\u0627\u062a",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "\u0627\u0644\u0641\u0646\u0627\u0646\u0648\u0646",
"Folders": "\u0627\u0644\u0645\u062c\u0644\u062f\u0627\u062a",
"Songs": "\u0627\u0644\u0623\u063a\u0627\u0646\u064a",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "\u062a\u0645 \u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645 {0}",
"UserPasswordChangedWithName": "\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0643\u0644\u0645\u0629 \u0627\u0644\u0633\u0631 \u0644\u0644\u0645\u0633\u062a\u062e\u062f\u0645 {0}",
"UserDeletedWithName": "\u062a\u0645 \u062d\u0630\u0641 \u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645 {0}",
- "UserConfigurationUpdatedWithName": "\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645 \u062a\u0645 \u062a\u062d\u062f\u064a\u062b\u0647\u0627 \u0644\u0640 {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u062e\u0627\u062f\u0645",
"MessageNamedServerConfigurationUpdatedWithValue": "\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u062e\u0627\u062f\u0645 \u0641\u064a \u0642\u0633\u0645 {0}",
"MessageApplicationUpdated": "\u0644\u0642\u062f \u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u062e\u0627\u062f\u0645 \u0623\u0645\u0628\u064a",
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 02c03f578..a80b6797a 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438",
"ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u043d\u0438 - {0}",
"Inherit": "\u041d\u0430\u0441\u043b\u0435\u0434\u044f\u0432\u0430\u043d\u0435",
@@ -24,6 +32,7 @@
"Channels": "\u041a\u0430\u043d\u0430\u043b\u0438",
"Movies": "\u0424\u0438\u043b\u043c\u0438",
"Albums": "\u0410\u043b\u0431\u0443\u043c\u0438",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "\u0418\u0437\u043f\u044a\u043b\u043d\u0438\u0442\u0435\u043b\u0438",
"Folders": "\u041f\u0430\u043f\u043a\u0438",
"Songs": "\u041f\u0435\u0441\u043d\u0438",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u043d",
"UserPasswordChangedWithName": "\u041f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f {0} \u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0435\u043d\u0430",
"UserDeletedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0438\u0437\u0442\u0440\u0438\u0442",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} \u0441\u0435 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e",
"UserOfflineFromDevice": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438 \u043e\u0442 {1}",
"DeviceOfflineWithName": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} \u043f\u0443\u0441\u043d\u0430 {1}",
+ "UserStoppedPlayingItemWithValues": "{0} \u0441\u043f\u0440\u044f {1}",
"NotificationOptionPluginError": "\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430",
"NotificationOptionApplicationUpdateAvailable": "\u041d\u0430\u043b\u0438\u0447\u043d\u043e \u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430",
"NotificationOptionApplicationUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e",
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 7c55540e5..8f3a2287c 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Darreres",
"ValueSpecialEpisodeName": "Especial - {0}",
"Inherit": "Heretat",
@@ -24,6 +32,7 @@
"Channels": "Canals",
"Movies": "Pel\u00b7l\u00edcules",
"Albums": "\u00c0lbums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artistes",
"Folders": "Directoris",
"Songs": "Can\u00e7ons",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}",
"UserDeletedWithName": "L'usuari {0} ha estat eliminat",
- "UserConfigurationUpdatedWithName": "La configuraci\u00f3 d'usuari ha estat actualitzada per a {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "S'ha actualitzat la configuraci\u00f3 del servidor",
"MessageNamedServerConfigurationUpdatedWithValue": "La secci\u00f3 de configuraci\u00f3 {0} ha estat actualitzada",
"MessageApplicationUpdated": "El Servidor d'Emby ha estat actualitzat",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index d59e40ed3..a78b3d3e3 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Nejnov\u011bj\u0161\u00ed",
"ValueSpecialEpisodeName": "Speci\u00e1l - {0}",
"Inherit": "Zd\u011bdit",
@@ -24,6 +32,7 @@
"Channels": "Kan\u00e1ly",
"Movies": "Filmy",
"Albums": "Alba",
+ "NameSeasonUnknown": "Nezn\u00e1m\u00e1 sez\u00f3na",
"Artists": "Um\u011blci",
"Folders": "Slo\u017eky",
"Songs": "Skladby",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "U\u017eivatel {0} byl vytvo\u0159en",
"UserPasswordChangedWithName": "Provedena zm\u011bna hesla pro u\u017eivatele {0}",
"UserDeletedWithName": "U\u017eivatel {0} byl smaz\u00e1n",
- "UserConfigurationUpdatedWithName": "Konfigurace u\u017eivatele byla aktualizov\u00e1na pro {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Konfigurace serveru aktualizov\u00e1na",
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizov\u00e1na",
"MessageApplicationUpdated": "Emby Server byl aktualizov\u00e1n",
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index eb05943f9..c3a782161 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Seneste",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Arv",
@@ -24,6 +32,7 @@
"Channels": "Kanaler",
"Movies": "Film",
"Albums": "Album",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Kunstner",
"Folders": "Mapper",
"Songs": "Sange",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserPasswordChangedWithName": "Adgangskode er \u00e6ndret for bruger {0}",
"UserDeletedWithName": "Brugeren {0} er blevet slettet",
- "UserConfigurationUpdatedWithName": "Brugerkonfiguration er blevet opdateret for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Serverkonfiguration er blevet opdateret",
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurationssektion {0} er blevet opdateret",
"MessageApplicationUpdated": "Emby Server er blevet opdateret",
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index bcfadb61c..1836ca5e7 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Kamera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} wurde ihrer Bibliothek hinzugef\u00fcgt",
+ "NameInstallFailed": "{0} Installation fehlgeschlagen",
+ "CameraImageUploadedFrom": "Ein neues Bild wurde hochgeladen von {0}",
+ "ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
+ "NewVersionIsAvailable": "Eine neue Version von Emby Server steht zum Download bereit.",
+ "MessageApplicationUpdatedTo": "Emby Server wurde auf Version {0} aktualisiert",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Neueste",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "\u00dcbernehmen",
@@ -24,6 +32,7 @@
"Channels": "Kan\u00e4le",
"Movies": "Filme",
"Albums": "Alben",
+ "NameSeasonUnknown": "Staffel unbekannt",
"Artists": "Interpreten",
"Folders": "Verzeichnisse",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserPasswordChangedWithName": "Das Passwort f\u00fcr Benutzer {0} wurde ge\u00e4ndert",
"UserDeletedWithName": "Benutzer {0} wurde gel\u00f6scht",
- "UserConfigurationUpdatedWithName": "Benutzereinstellungen wurden aktualisiert f\u00fcr {0}",
+ "UserPolicyUpdatedWithName": "Benutzerrichtlinie wurde f\u00fcr {0} aktualisiert",
"MessageServerConfigurationUpdated": "Server Einstellungen wurden aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server Einstellungsbereich {0} wurde aktualisiert",
"MessageApplicationUpdated": "Emby Server wurde auf den neusten Stand gebracht.",
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index ab229e111..c81f3d2ac 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -1,6 +1,14 @@
{
- "Latest": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1",
- "ValueSpecialEpisodeName": "\u0395\u03b9\u03b4\u03b9\u03ba\u03ac - {0} ",
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} \u03c0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd \u03c3\u03b1\u03c2",
+ "NameInstallFailed": "{0} \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5",
+ "CameraImageUploadedFrom": "\u039c\u03b9\u03b1 \u03bd\u03ad\u03b1 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03bf\u03c3\u03c4\u03b1\u03bb\u03b5\u03af \u03b1\u03c0\u03cc {0}",
+ "ServerNameNeedsToBeRestarted": "{0} \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7",
+ "NewVersionIsAvailable": "\u039c\u03b9\u03b1 \u03bd\u03ad\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 Emby Server \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b3\u03b9\u03b1 \u03bb\u03ae\u03c8\u03b7.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "Latest": "\u03a0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03b1",
+ "ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
"Books": "\u0392\u03b9\u03b2\u03bb\u03af\u03b1",
"Music": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae",
@@ -8,15 +16,15 @@
"Photos": "\u03a6\u03c9\u03c4\u03bf\u03b3\u03c1\u03b1\u03c6\u03af\u03b5\u03c2",
"MixedContent": "\u0391\u03bd\u03ac\u03bc\u03b5\u03b9\u03ba\u03c4\u03bf \u03a0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf",
"MusicVideos": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ac \u03b2\u03af\u03bd\u03c4\u03b5\u03bf",
- "HomeVideos": "\u03a0\u03c1\u03bf\u03c3\u03c9\u03c0\u03b9\u03ba\u03ac \u0392\u03af\u03bd\u03c4\u03b5\u03bf",
+ "HomeVideos": "\u03a0\u03c1\u03bf\u03c3\u03c9\u03c0\u03b9\u03ba\u03ac \u03b2\u03af\u03bd\u03c4\u03b5\u03bf",
"Playlists": "\u039b\u03af\u03c3\u03c4\u03b5\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2",
"HeaderRecordingGroups": "\u0393\u03ba\u03c1\u03bf\u03c5\u03c0 \u0395\u03b3\u03b3\u03c1\u03b1\u03c6\u03ce\u03bd",
- "HeaderContinueWatching": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7",
+ "HeaderContinueWatching": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03b5\u03af\u03c4\u03b5",
"HeaderFavoriteArtists": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03bf\u03b9 \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2",
"HeaderFavoriteSongs": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1",
"HeaderAlbumArtists": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03b5\u03c7\u03bd\u03ce\u03bd",
"HeaderFavoriteAlbums": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc",
- "HeaderFavoriteEpisodes": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03b5\u03c0\u03b5\u03b9\u03c3\u03cc\u03b4\u03b9\u03b1",
+ "HeaderFavoriteEpisodes": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u0395\u03c0\u03b5\u03b9\u03c3\u03cc\u03b4\u03b9\u03b1",
"HeaderFavoriteShows": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03a3\u03b5\u03b9\u03c1\u03ad\u03c2",
"HeaderNextUp": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf",
"Favorites": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1",
@@ -24,10 +32,11 @@
"Channels": "\u039a\u03b1\u03bd\u03ac\u03bb\u03b9\u03b1",
"Movies": "\u03a4\u03b1\u03b9\u03bd\u03af\u03b5\u03c2",
"Albums": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc",
+ "NameSeasonUnknown": "\u0386\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf\u03c2 \u039a\u03cd\u03ba\u03bb\u03bf\u03c2",
"Artists": "\u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2",
"Folders": "\u03a6\u03ac\u03ba\u03b5\u03bb\u03bf\u03b9",
"Songs": "\u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1",
- "TvShows": "\u03a4\u03b7\u03bb\u03b5\u03bf\u03c0\u03c4\u03b9\u03ba\u03ac \u03c0\u03c1\u03bf\u03b3\u03c1\u03ac\u03bc\u03bc\u03b1\u03c4\u03b1",
+ "TvShows": "\u03a4\u03b7\u03bb\u03b5\u03bf\u03c0\u03c4\u03b9\u03ba\u03ad\u03c2 \u03a3\u03b5\u03b9\u03c1\u03ad\u03c2",
"Shows": "\u03a3\u03b5\u03b9\u03c1\u03ad\u03c2",
"Genres": "\u0395\u03af\u03b4\u03b7",
"NameSeasonNumber": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2 {0}",
@@ -35,57 +44,57 @@
"UserDownloadingItemWithValues": "{0} \u03ba\u03b1\u03c4\u03b5\u03b2\u03ac\u03b6\u03b5\u03b9 {1}",
"HeaderLiveTV": "\u0396\u03c9\u03bd\u03c4\u03b1\u03bd\u03ae \u03a4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7",
"ChapterNameValue": "\u039a\u03b5\u03c6\u03ac\u03bb\u03b1\u03b9\u03bf {0}",
- "ScheduledTaskFailedWithName": "{0} \u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1",
+ "ScheduledTaskFailedWithName": "{0} \u03b1\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1",
"LabelRunningTimeValue": "\u0394\u03b9\u03ac\u03c1\u03ba\u03b5\u03b9\u03b1: {0}",
- "ScheduledTaskStartedWithName": "{0} \u03ad\u03bd\u03b1\u03c1\u03be\u03b7",
+ "ScheduledTaskStartedWithName": "{0} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
"VersionNumber": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 {0}",
"PluginInstalledWithName": "{0} \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03b8\u03b7\u03ba\u03b5",
- "StartupEmbyServerIsLoading": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03c6\u03bf\u03c1\u03c4\u03ce\u03bd\u03b5\u03b9. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03c3\u03b5 \u03bb\u03af\u03b3\u03bf",
+ "StartupEmbyServerIsLoading": "\u039f Emby Server \u03c6\u03bf\u03c1\u03c4\u03ce\u03bd\u03b5\u03b9. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03c3\u03b5 \u03bb\u03af\u03b3\u03bf.",
"PluginUpdatedWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af",
"PluginUninstalledWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b1\u03b8\u03b5\u03af",
"ItemAddedWithName": "{0} \u03c0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7",
"ItemRemovedWithName": "{0} \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c6\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7",
"LabelIpAddressValue": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP: {0}",
"DeviceOnlineWithName": "{0} \u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5",
- "UserOnlineFromDevice": "{0} \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b1\u03c0\u03bf {1}",
- "ProviderValue": "\u03a0\u03ac\u03c1\u03bf\u03c7\u03bf\u03c2: {0}",
- "SubtitlesDownloadedForItem": "\u03a5\u03c0\u03cc\u03c4\u03b9\u03c4\u03bb\u03bf\u03b9 \u03bb\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03b1\u03c0\u03cc {0}",
+ "UserOnlineFromDevice": "{0} \u03b5\u03af\u03bd\u03b1\u03b9 online \u03b1\u03c0\u03bf {1}",
+ "ProviderValue": "Provider: {0}",
+ "SubtitlesDownloadedForItem": "\u039f\u03b9 \u03c5\u03c0\u03cc\u03c4\u03b9\u03c4\u03bb\u03bf\u03b9 \u03ba\u03b1\u03c4\u03ad\u03b2\u03b7\u03ba\u03b1\u03bd \u03b3\u03b9\u03b1 {0}",
"UserCreatedWithName": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03bf \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0}",
- "UserPasswordChangedWithName": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03b1\u03bb\u03bb\u03ac\u03c7\u03b8\u03b7\u03ba\u03b5",
- "UserDeletedWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b4\u03b9\u03b5\u03b3\u03c1\u03ac\u03c6\u03b5\u03b9",
- "UserConfigurationUpdatedWithName": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
- "MessageNamedServerConfigurationUpdatedWithValue": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03bc\u03ad\u03b1 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9",
- "MessageApplicationUpdated": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af",
- "FailedLoginAttemptWithUserName": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b1\u03c0\u03cc {0}",
+ "UserPasswordChangedWithName": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9",
+ "UserDeletedWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03b5\u03af",
+ "UserPolicyUpdatedWithName": "\u0397 \u03c0\u03bf\u03bb\u03b9\u03c4\u03b9\u03ba\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03b8\u03b5\u03af \u03b3\u03b9\u03b1 {0}",
+ "MessageServerConfigurationUpdated": "\u0397 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c4\u03bf\u03c5 server \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03b8\u03b5\u03af",
+ "MessageNamedServerConfigurationUpdatedWithValue": "\u0397 \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 {0} \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c4\u03bf\u03c5 server \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03b8\u03b5\u03af",
+ "MessageApplicationUpdated": "\u039f Emby Server \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03b8\u03b5\u03af",
+ "FailedLoginAttemptWithUserName": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03b7\u03bc\u03ad\u03bd\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc {0}",
"AuthenticationSucceededWithUserName": "{0} \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03b5\u03af\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7",
"UserOfflineFromDevice": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc {1}",
"DeviceOfflineWithName": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5",
- "UserStartedPlayingItemWithValues": "{0} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}",
- "UserStoppedPlayingItemWithValues": "{0} \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}",
- "NotificationOptionPluginError": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c4\u03bf\u03c5",
- "NotificationOptionApplicationUpdateAvailable": "\u03a5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7",
- "NotificationOptionApplicationUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5",
- "NotificationOptionPluginUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 plugin \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5",
+ "UserStartedPlayingItemWithValues": "{0} \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1} \u03c3\u03b5 {2}",
+ "UserStoppedPlayingItemWithValues": "{0} \u03c4\u03b5\u03bb\u03b5\u03af\u03c9\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1} \u03c3\u03b5 {2}",
+ "NotificationOptionPluginError": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c4\u03bf\u03c5 plugin",
+ "NotificationOptionApplicationUpdateAvailable": "\u0394\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03bc\u03ad\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2",
+ "NotificationOptionApplicationUpdateInstalled": "\u0397 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
+ "NotificationOptionPluginUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 plugin \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
"NotificationOptionPluginInstalled": "\u03a4\u03bf plugin \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
"NotificationOptionPluginUninstalled": "\u03a4\u03bf plugin \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
- "NotificationOptionVideoPlayback": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c0\u03c1\u03bf\u03b2\u03ac\u03bb\u03bb\u03b5\u03c4\u03b1\u03b9",
- "NotificationOptionAudioPlayback": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c0\u03b1\u03af\u03b6\u03b5\u03b9",
- "NotificationOptionGamePlayback": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
- "NotificationOptionVideoPlaybackStopped": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
- "NotificationOptionAudioPlaybackStopped": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
- "NotificationOptionGamePlaybackStopped": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+ "NotificationOptionVideoPlayback": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
+ "NotificationOptionAudioPlayback": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03ae\u03c7\u03bf\u03c5 \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
+ "NotificationOptionGamePlayback": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03bf\u03c5 \u03c0\u03b1\u03b9\u03c7\u03bd\u03b9\u03b4\u03b9\u03bf\u03cd \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
+ "NotificationOptionVideoPlaybackStopped": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+ "NotificationOptionAudioPlaybackStopped": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03ae\u03c7\u03bf\u03c5 \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+ "NotificationOptionGamePlaybackStopped": "\u0397 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03bf\u03c5 \u03c0\u03b1\u03b9\u03c7\u03bd\u03b9\u03b4\u03b9\u03bf\u03cd \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
"NotificationOptionTaskFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2",
"NotificationOptionInstallationFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2",
"NotificationOptionNewLibraryContent": "\u03a0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03bd\u03ad\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionUserLockedOut": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
- "NotificationOptionServerRestartRequired": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae",
+ "NotificationOptionServerRestartRequired": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 server",
"UserLockedOutWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
- "SubtitleDownloadFailureForItem": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c5\u03c0\u03bf\u03c4\u03af\u03c4\u03bb\u03c9\u03bd \u03b1\u03c0\u03cc {0}",
+ "SubtitleDownloadFailureForItem": "\u039f\u03b9 \u03c5\u03c0\u03cc\u03c4\u03b9\u03c4\u03bb\u03bf\u03b9 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b1\u03bd \u03bd\u03b1 \u03ba\u03b1\u03c4\u03ad\u03b2\u03bf\u03c5\u03bd \u03b3\u03b9\u03b1 {0}",
"Sync": "\u03a3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2",
"User": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2",
"System": "\u03a3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1",
"Application": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae",
- "Plugin": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf"
+ "Plugin": "Plugin"
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 62db5a358..6e3d29b13 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 9c58b4539..a97848f8a 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -24,6 +24,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -44,6 +45,7 @@
"PluginUpdatedWithName": "{0} was updated",
"PluginUninstalledWithName": "{0} was uninstalled",
"ItemAddedWithName": "{0} was added to the library",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "Ip address: {0}",
"DeviceOnlineWithName": "{0} is connected",
@@ -53,7 +55,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +63,9 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
@@ -82,10 +85,15 @@
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionServerRestartRequired": "Server restart required",
"UserLockedOutWithName": "User {0} has been locked out",
- "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "NameInstallFailed": "{0} installation failed",
"Sync": "Sync",
"User": "User",
"System": "System",
"Application": "Application",
- "Plugin": "Plugin"
+ "Plugin": "Plugin",
+ "HeaderCameraUploads": "Camera Uploads",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download."
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index c48042d9a..17d9c80a0 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 8bfaffec8..704566550 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Subidos desde Camara",
+ "ValueHasBeenAddedToLibrary": "{0} se han a\u00f1adido a su biblioteca de medios",
+ "NameInstallFailed": "{0} instalaci\u00f3n fallida",
+ "CameraImageUploadedFrom": "Una nueva imagen de c\u00e1mara ha sido subida desde {0}",
+ "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
+ "NewVersionIsAvailable": "Una nueva versi\u00f3n del Servidor Emby est\u00e1 disponible para descargar.",
+ "MessageApplicationUpdatedTo": "El servidor Emby ha sido actualizado a {0}",
+ "SubtitleDownloadFailureFromForItem": "Fall\u00f3 la descarga de subtitulos desde {0} para {1}",
"Latest": "Recientes",
"ValueSpecialEpisodeName": "Especial - {0}",
"Inherit": "Heredar",
@@ -24,6 +32,7 @@
"Channels": "Canales",
"Movies": "Pel\u00edculas",
"Albums": "\u00c1lbumes",
+ "NameSeasonUnknown": "Temporada Desconocida",
"Artists": "Artistas",
"Folders": "Carpetas",
"Songs": "Canciones",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Se ha creado el usuario {0}",
"UserPasswordChangedWithName": "Se ha cambiado la contrase\u00f1a para el usuario {0}",
"UserDeletedWithName": "Se ha eliminado el usuario {0}",
- "UserConfigurationUpdatedWithName": "Se ha actualizado la configuraci\u00f3n del usuario {0}",
+ "UserPolicyUpdatedWithName": "Las pol\u00edtica de usuario ha sido actualizada por {0}",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuraci\u00f3n del servidor",
"MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la secci\u00f3n {0} de la configuraci\u00f3n del servidor",
"MessageApplicationUpdated": "El servidor Emby ha sido actualizado",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} autenticado con \u00e9xito",
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"DeviceOfflineWithName": "{0} se ha desconectado",
- "UserStartedPlayingItemWithValues": "{0} ha iniciado la reproducci\u00f3n de {1}",
- "UserStoppedPlayingItemWithValues": "{0} ha detenido la reproducci\u00f3n de {1}",
+ "UserStartedPlayingItemWithValues": "{0} est\u00e1 reproduci\u00e9ndose {1} en {2}",
+ "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}",
"NotificationOptionPluginError": "Falla de complemento",
"NotificationOptionApplicationUpdateAvailable": "Actualizaci\u00f3n de aplicaci\u00f3n disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualizaci\u00f3n de aplicaci\u00f3n instalada",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index c7fa51c03..eb9e75054 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u00daltimos",
"ValueSpecialEpisodeName": "Especial - {0}",
"Inherit": "Heredar",
@@ -24,6 +32,7 @@
"Channels": "Canales",
"Movies": "Peliculas",
"Albums": "\u00c1lbumes",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artistas",
"Folders": "Carpetas",
"Songs": "Canciones",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserPasswordChangedWithName": "Se ha cambiado la contrase\u00f1a para el usuario {0}",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
- "UserConfigurationUpdatedWithName": "Configuraci\u00f3n de usuario se ha actualizado para {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuraci\u00f3n del servidor",
"MessageNamedServerConfigurationUpdatedWithValue": "La secci\u00f3n de configuraci\u00f3n del servidor {0} ha sido actualizado",
"MessageApplicationUpdated": "Se ha actualizado el servidor Emby",
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
new file mode 100644
index 000000000..3d494328a
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -0,0 +1,100 @@
+{
+ "HeaderCameraUploads": "\u0622\u067e\u0644\u0648\u062f\u0647\u0627\u06cc \u062f\u0648\u0631\u0628\u06cc\u0646",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "Latest": "\u0622\u062e\u0631\u06cc\u0646",
+ "ValueSpecialEpisodeName": "\u0648\u06cc\u0698\u0647- {0}",
+ "Inherit": "\u0628\u0647 \u0627\u0631\u062b \u0628\u0631\u062f\u0647",
+ "Books": "\u06a9\u062a\u0627\u0628 \u0647\u0627",
+ "Music": "\u0645\u0648\u0633\u06cc\u0642\u06cc",
+ "Games": "\u0628\u0627\u0632\u06cc \u0647\u0627",
+ "Photos": "\u0639\u06a9\u0633 \u0647\u0627",
+ "MixedContent": "\u0645\u062d\u062a\u0648\u0627\u06cc \u062f\u0631\u0647\u0645",
+ "MusicVideos": "\u0645\u0648\u0632\u06cc\u06a9 \u0648\u06cc\u062f\u06cc\u0648\u0647\u0627",
+ "HomeVideos": "\u0648\u06cc\u062f\u06cc\u0648\u0647\u0627\u06cc \u062e\u0627\u0646\u06af\u06cc",
+ "Playlists": "\u0644\u06cc\u0633\u062a \u0647\u0627\u06cc \u067e\u062e\u0634",
+ "HeaderRecordingGroups": "\u06af\u0631\u0648\u0647 \u0647\u0627\u06cc \u0636\u0628\u0637",
+ "HeaderContinueWatching": "\u0627\u062f\u0627\u0645\u0647 \u062a\u0645\u0627\u0634\u0627",
+ "HeaderFavoriteArtists": "\u0647\u0646\u0631\u0645\u0646\u062f\u0627\u0646 \u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647",
+ "HeaderFavoriteSongs": "\u0622\u0647\u0646\u06af \u0647\u0627\u06cc \u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647",
+ "HeaderAlbumArtists": "\u0647\u0646\u0631\u0645\u0646\u062f\u0627\u0646 \u0622\u0644\u0628\u0648\u0645",
+ "HeaderFavoriteAlbums": "\u0622\u0644\u0628\u0648\u0645 \u0647\u0627\u06cc \u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647",
+ "HeaderFavoriteEpisodes": "\u0642\u0633\u0645\u062a \u0647\u0627\u06cc \u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647",
+ "HeaderFavoriteShows": "\u0633\u0631\u06cc\u0627\u0644 \u0647\u0627\u06cc \u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647",
+ "HeaderNextUp": "\u0628\u0639\u062f\u06cc \u0686\u06cc\u0647",
+ "Favorites": "\u0645\u0648\u0631\u062f \u0639\u0644\u0627\u0642\u0647 \u0647\u0627",
+ "Collections": "\u06a9\u0644\u06a9\u0633\u06cc\u0648\u0646 \u0647\u0627",
+ "Channels": "\u06a9\u0627\u0646\u0627\u0644 \u0647\u0627",
+ "Movies": "\u0641\u06cc\u0644\u0645 \u0647\u0627\u06cc \u0633\u06cc\u0646\u0645\u0627\u06cc\u06cc",
+ "Albums": "\u0622\u0644\u0628\u0648\u0645 \u0647\u0627",
+ "NameSeasonUnknown": "\u0641\u0635\u0644 \u0647\u0627\u06cc \u0646\u0627\u0634\u0646\u0627\u062e\u062a\u0647",
+ "Artists": "\u0647\u0646\u0631\u0645\u0646\u062f\u0627\u0646",
+ "Folders": "\u067e\u0648\u0634\u0647 \u0647\u0627",
+ "Songs": "\u0622\u0647\u0646\u06af \u0647\u0627",
+ "TvShows": "\u0633\u0631\u06cc\u0627\u0644 \u0647\u0627\u06cc \u062a\u0644\u0648\u06cc\u0632\u06cc\u0648\u0646\u06cc",
+ "Shows": "\u0633\u0631\u06cc\u0627\u0644 \u0647\u0627",
+ "Genres": "\u0698\u0627\u0646\u0631\u0647\u0627",
+ "NameSeasonNumber": "\u0641\u0635\u0644 {0}",
+ "AppDeviceValues": "\u0628\u0631\u0646\u0627\u0645\u0647: {0} \u060c \u062f\u0633\u062a\u06af\u0627\u0647: {1}",
+ "UserDownloadingItemWithValues": "{0} \u062f\u0631 \u062d\u0627\u0644 \u062f\u0627\u0646\u0644\u0648\u062f \u0627\u0633\u062a {1}",
+ "HeaderLiveTV": "\u067e\u062e\u0634 \u0632\u0646\u062f\u0647 \u062a\u0644\u0648\u06cc\u0632\u06cc\u0648\u0646",
+ "ChapterNameValue": "\u0641\u0635\u0644 {0}",
+ "ScheduledTaskFailedWithName": "{0} \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062f",
+ "LabelRunningTimeValue": "\u0632\u0645\u0627\u0646 \u0627\u062c\u0631\u0627: {0}",
+ "ScheduledTaskStartedWithName": "{0} \u0634\u0631\u0648\u0639 \u0634\u062f",
+ "VersionNumber": "\u0646\u0633\u062e\u0647 {0}",
+ "PluginInstalledWithName": "{0} \u0646\u0635\u0628 \u0634\u062f",
+ "StartupEmbyServerIsLoading": "\u0633\u0631\u0648\u0631 Emby \u062f\u0631 \u062d\u0627\u0644 \u0628\u0627\u0631\u06af\u06cc\u0631\u06cc \u0627\u0633\u062a. \u0644\u0637\u0641\u0627 \u06a9\u0645\u06cc \u0628\u0639\u062f \u062f\u0648\u0628\u0627\u0631\u0647 \u062a\u0644\u0627\u0634 \u06a9\u0646\u06cc\u062f.",
+ "PluginUpdatedWithName": "{0} \u0622\u067e\u062f\u06cc\u062a \u0634\u062f",
+ "PluginUninstalledWithName": "{0} \u062d\u0630\u0641 \u0634\u062f",
+ "ItemAddedWithName": "{0} \u0628\u0647 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0627\u0641\u0632\u0648\u062f\u0647 \u0634\u062f",
+ "ItemRemovedWithName": "{0} \u0627\u0632 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u062d\u0630\u0641 \u0634\u062f",
+ "LabelIpAddressValue": "\u0622\u062f\u0631\u0633 \u0622\u06cc \u067e\u06cc: {0}",
+ "DeviceOnlineWithName": "{0} \u0645\u062a\u0635\u0644 \u0634\u062f\u0647",
+ "UserOnlineFromDevice": "{0}\u0627\u0632 {1} \u0622\u0646\u0644\u0627\u06cc\u0646 \u0645\u06cc\u0628\u0627\u0634\u062f",
+ "ProviderValue": "\u0627\u0631\u0627\u0626\u0647 \u062f\u0647\u0646\u062f\u0647: {0}",
+ "SubtitlesDownloadedForItem": "\u0632\u06cc\u0631\u0646\u0648\u06cc\u0633 {0} \u062f\u0627\u0646\u0644\u0648\u062f \u0634\u062f",
+ "UserCreatedWithName": "\u06a9\u0627\u0631\u0628\u0631 {0} \u0627\u06cc\u062c\u0627\u062f \u0634\u062f",
+ "UserPasswordChangedWithName": "\u0631\u0645\u0632 \u0628\u0631\u0627\u06cc \u06a9\u0627\u0631\u0628\u0631 {0} \u062a\u063a\u06cc\u06cc\u0631 \u06cc\u0627\u0641\u062a",
+ "UserDeletedWithName": "\u06a9\u0627\u0631\u0628\u0631 {0} \u062d\u0630\u0641 \u0634\u062f",
+ "UserPolicyUpdatedWithName": "\u0633\u06cc\u0627\u0633\u062a \u06a9\u0627\u0631\u0628\u0631\u06cc \u0628\u0631\u0627\u06cc {0} \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0634\u062f",
+ "MessageServerConfigurationUpdated": "\u067e\u06cc\u06a9\u0631\u0628\u0646\u062f\u06cc \u0633\u0631\u0648\u0631 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0634\u062f",
+ "MessageNamedServerConfigurationUpdatedWithValue": "\u067e\u06a9\u0631\u0628\u0646\u062f\u06cc \u0628\u062e\u0634 {0} \u0633\u0631\u0648\u0631 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0634\u062f",
+ "MessageApplicationUpdated": "\u0633\u0631\u0648\u0631 Emby \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0634\u062f",
+ "FailedLoginAttemptWithUserName": "\u062a\u0644\u0627\u0634 \u0628\u0631\u0627\u06cc \u0648\u0631\u0648\u062f \u0627\u0632 {0} \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062f",
+ "AuthenticationSucceededWithUserName": "{0} \u0628\u0627 \u0645\u0648\u0641\u0642\u06cc\u062a \u062a\u0627\u06cc\u06cc\u062f \u0627\u0639\u062a\u0628\u0627\u0631 \u0634\u062f",
+ "UserOfflineFromDevice": "\u0627\u0631\u062a\u0628\u0627\u0637 {0} \u0627\u0632 {1} \u0642\u0637\u0639 \u0634\u062f",
+ "DeviceOfflineWithName": "\u0627\u0631\u062a\u0628\u0627\u0637 {0} \u0642\u0637\u0639 \u0634\u062f",
+ "UserStartedPlayingItemWithValues": "{0} \u0634\u0631\u0648\u0639 \u0628\u0647 \u067e\u062e\u0634 {1} \u06a9\u0631\u062f",
+ "UserStoppedPlayingItemWithValues": "{0} \u067e\u062e\u0634 {1} \u0631\u0627 \u0645\u062a\u0648\u0642\u0641 \u06a9\u0631\u062f",
+ "NotificationOptionPluginError": "\u062e\u0631\u0627\u0628\u06cc \u0627\u0641\u0632\u0648\u0646\u0647",
+ "NotificationOptionApplicationUpdateAvailable": "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0645\u0648\u062c\u0648\u062f \u0627\u0633\u062a",
+ "NotificationOptionApplicationUpdateInstalled": "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0635\u0628 \u0634\u062f",
+ "NotificationOptionPluginUpdateInstalled": "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0627\u0641\u0632\u0648\u0646\u0647 \u0646\u0635\u0628 \u0634\u062f",
+ "NotificationOptionPluginInstalled": "\u0627\u0641\u0632\u0648\u0646\u0647 \u0646\u0635\u0628 \u0634\u062f",
+ "NotificationOptionPluginUninstalled": "\u0627\u0641\u0632\u0648\u0646\u0647 \u062d\u0630\u0641 \u0634\u062f",
+ "NotificationOptionVideoPlayback": "\u067e\u062e\u0634 \u0648\u06cc\u062f\u06cc\u0648 \u0622\u063a\u0627\u0632 \u0634\u062f",
+ "NotificationOptionAudioPlayback": "\u067e\u062e\u0634 \u0635\u062f\u0627 \u0622\u063a\u0627\u0632 \u0634\u062f",
+ "NotificationOptionGamePlayback": "\u067e\u062e\u0634 \u0628\u0627\u0632\u06cc \u0622\u063a\u0627\u0632 \u0634\u062f",
+ "NotificationOptionVideoPlaybackStopped": "\u067e\u062e\u0634 \u0648\u06cc\u062f\u06cc\u0648 \u0645\u062a\u0648\u0642\u0641 \u0634\u062f",
+ "NotificationOptionAudioPlaybackStopped": "\u067e\u062e\u0634 \u0635\u062f\u0627 \u0645\u062a\u0648\u0642\u0641 \u0634\u062f",
+ "NotificationOptionGamePlaybackStopped": "\u067e\u062e\u0634 \u0628\u0627\u0632\u06cc \u0645\u062a\u0648\u0642\u0641 \u0634\u062f",
+ "NotificationOptionTaskFailed": "\u0634\u06a9\u0633\u062a \u0648\u0638\u06cc\u0641\u0647 \u0628\u0631\u0646\u0627\u0645\u0647 \u0631\u06cc\u0632\u06cc \u0634\u062f\u0647",
+ "NotificationOptionInstallationFailed": "\u0634\u06a9\u0633\u062a \u0646\u0635\u0628",
+ "NotificationOptionNewLibraryContent": "\u0645\u062d\u062a\u0648\u0627\u06cc \u062c\u062f\u06cc\u062f \u0627\u0641\u0632\u0648\u062f\u0647 \u0634\u062f",
+ "NotificationOptionCameraImageUploaded": "\u062a\u0635\u0627\u0648\u06cc\u0631 \u062f\u0648\u0631\u0628\u06cc\u0646 \u0622\u067e\u0644\u0648\u062f \u0634\u062f",
+ "NotificationOptionUserLockedOut": "\u06a9\u0627\u0631\u0628\u0631 \u0627\u0632 \u0633\u06cc\u0633\u062a\u0645 \u062e\u0627\u0631\u062c \u0634\u062f",
+ "NotificationOptionServerRestartRequired": "\u0634\u0631\u0648\u0639 \u0645\u062c\u062f\u062f \u0633\u0631\u0648\u0631 \u0646\u06cc\u0627\u0632 \u0627\u0633\u062a",
+ "UserLockedOutWithName": "\u06a9\u0627\u0631\u0628\u0631 {0} \u0627\u0632 \u0633\u06cc\u0633\u062a\u0645 \u062e\u0627\u0631\u062c \u0634\u062f",
+ "SubtitleDownloadFailureForItem": "\u062f\u0627\u0646\u0644\u0648\u062f \u0632\u06cc\u0631\u0646\u0648\u06cc\u0633 \u0628\u0631\u0627\u06cc {0} \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062f",
+ "Sync": "\u0647\u0645\u06af\u0627\u0645\u0633\u0627\u0632\u06cc",
+ "User": "\u06a9\u0627\u0631\u0628\u0631",
+ "System": "\u0633\u06cc\u0633\u062a\u0645",
+ "Application": "\u0628\u0631\u0646\u0627\u0645\u0647",
+ "Plugin": "\u0627\u0641\u0632\u0648\u0646\u0647"
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index 7743905f0..1acee0ae9 100644
--- a/Emby.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Sp\u00e9cial - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 6552a47ab..30fe22ac2 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -1,5 +1,13 @@
{
- "Latest": "R\u00e9cent",
+ "HeaderCameraUploads": "Photos transf\u00e9r\u00e9es",
+ "ValueHasBeenAddedToLibrary": "{0} a \u00e9t\u00e9 ajout\u00e9 \u00e0 votre librairie",
+ "NameInstallFailed": "{0} \u00e9chec d'installation",
+ "CameraImageUploadedFrom": "Une image de cam\u00e9ra a \u00e9t\u00e9 charg\u00e9e depuis {0}",
+ "ServerNameNeedsToBeRestarted": "{0} doit \u00eatre red\u00e9marr\u00e9",
+ "NewVersionIsAvailable": "Une nouvelle version d'Emby Serveur est disponible au t\u00e9l\u00e9chargement.",
+ "MessageApplicationUpdatedTo": "Emby Serveur a \u00e9t\u00e9 mis \u00e0 jour en version {0}",
+ "SubtitleDownloadFailureFromForItem": "\u00c9chec du t\u00e9l\u00e9chargement des sous-titres depuis {0} pour {1}",
+ "Latest": "Derniers",
"ValueSpecialEpisodeName": "Sp\u00e9cial - {0}",
"Inherit": "H\u00e9riter",
"Books": "Livres",
@@ -24,6 +32,7 @@
"Channels": "Cha\u00eenes",
"Movies": "Films",
"Albums": "Albums",
+ "NameSeasonUnknown": "Saison Inconnue",
"Artists": "Artistes",
"Folders": "Dossiers",
"Songs": "Chansons",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "L'utilisateur {0} a \u00e9t\u00e9 cr\u00e9\u00e9",
"UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a \u00e9t\u00e9 modifi\u00e9",
"UserDeletedWithName": "L'utilisateur {0} a \u00e9t\u00e9 supprim\u00e9",
- "UserConfigurationUpdatedWithName": "La configuration utilisateur de {0} a \u00e9t\u00e9 mise \u00e0 jour",
+ "UserPolicyUpdatedWithName": "La politique de l'utilisateur a \u00e9t\u00e9 mise \u00e0 jour pour {0}",
"MessageServerConfigurationUpdated": "La configuration du serveur a \u00e9t\u00e9 mise \u00e0 jour.",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a \u00e9t\u00e9 mise \u00e0 jour",
"MessageApplicationUpdated": "Le serveur Emby a \u00e9t\u00e9 mis \u00e0 jour",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} s'est authentifi\u00e9 avec succ\u00e8s",
"UserOfflineFromDevice": "{0} s'est d\u00e9connect\u00e9 depuis {1}",
"DeviceOfflineWithName": "{0} s'est d\u00e9connect\u00e9",
- "UserStartedPlayingItemWithValues": "{0} vient de commencer la lecture de {1}",
- "UserStoppedPlayingItemWithValues": "{0} vient d'arr\u00eater la lecture de {1}",
+ "UserStartedPlayingItemWithValues": "{0} est entrain de lire {1} sur {2}",
+ "UserStoppedPlayingItemWithValues": "{0} vient d'arr\u00eater la lecture de {1} sur {2}",
"NotificationOptionPluginError": "Erreur d'extension",
"NotificationOptionApplicationUpdateAvailable": "Mise \u00e0 jour de l'application disponible",
"NotificationOptionApplicationUpdateInstalled": "Mise \u00e0 jour de l'application install\u00e9e",
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 5c7ff5d6d..4f51a5ef4 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Letschte",
"ValueSpecialEpisodeName": "Spezial - {0}",
"Inherit": "Hinzuef\u00fcege",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index c679ed289..c3d1ee7ae 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -1,17 +1,25 @@
{
- "Latest": "Latest",
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "Latest": "\u05d0\u05d7\u05e8\u05d5\u05df",
"ValueSpecialEpisodeName": "\u05de\u05d9\u05d5\u05d7\u05d3- {0}",
"Inherit": "Inherit",
- "Books": "Books",
- "Music": "Music",
- "Games": "Games",
- "Photos": "Photos",
- "MixedContent": "Mixed content",
+ "Books": "\u05e1\u05e4\u05e8\u05d9\u05dd",
+ "Music": "\u05de\u05d5\u05d6\u05d9\u05e7\u05d4",
+ "Games": "\u05de\u05e9\u05d7\u05e7\u05d9\u05dd",
+ "Photos": "\u05ea\u05de\u05d5\u05e0\u05d5\u05ea",
+ "MixedContent": "\u05ea\u05d5\u05db\u05df \u05de\u05e2\u05d5\u05e8\u05d1",
"MusicVideos": "Music videos",
"HomeVideos": "Home videos",
- "Playlists": "Playlists",
- "HeaderRecordingGroups": "Recording Groups",
- "HeaderContinueWatching": "Continue Watching",
+ "Playlists": "\u05e8\u05e9\u05d9\u05de\u05d5\u05ea \u05e0\u05d9\u05d2\u05d5\u05df",
+ "HeaderRecordingGroups": "\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05d4\u05e7\u05dc\u05d8\u05d4",
+ "HeaderContinueWatching": "\u05d4\u05de\u05e9\u05da \u05d1\u05e6\u05e4\u05d9\u05d9\u05d4",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteSongs": "Favorite Songs",
"HeaderAlbumArtists": "Album Artists",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "\u05e1\u05e8\u05d8\u05d9\u05dd",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index c807e53b4..47c41d97c 100644
--- a/Emby.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Najnovije",
"ValueSpecialEpisodeName": "Specijal - {0}",
"Inherit": "Naslijedi",
@@ -24,6 +32,7 @@
"Channels": "Kanali",
"Movies": "Filmovi",
"Albums": "Albumi",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Izvo\u0111a\u010di",
"Folders": "Mape",
"Songs": "Pjesme",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Korisnik {0} je stvoren",
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
"UserDeletedWithName": "Korisnik {0} je obrisan",
- "UserConfigurationUpdatedWithName": "Postavke korisnika su a\u017eurirane za {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Postavke servera su a\u017eurirane",
"MessageNamedServerConfigurationUpdatedWithValue": "Odjeljak postavka servera {0} je a\u017euriran",
"MessageApplicationUpdated": "Emby Server je a\u017euriran",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index dc8f2b702..5d90d03f2 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Leg\u00fajabb",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,11 +32,12 @@
"Channels": "Csatorn\u00e1k",
"Movies": "Filmek",
"Albums": "Albumok",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "El\u0151ad\u00f3k",
"Folders": "K\u00f6nyvt\u00e1rak",
"Songs": "Dalok",
- "TvShows": "TV Shows",
- "Shows": "Shows",
+ "TvShows": "TV M\u0171sorok",
+ "Shows": "M\u0171sorok",
"Genres": "M\u0171fajok",
"NameSeasonNumber": "Season {0}",
"AppDeviceValues": "Program: {0}, Eszk\u00f6z: {1}",
@@ -53,18 +62,18 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
- "MessageServerConfigurationUpdated": "Szerver konfigur\u00e1ci\u00f3 friss\u00fclt",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
+ "MessageServerConfigurationUpdated": "Szerver konfigur\u00e1ci\u00f3 friss\u00edtve",
"MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigur\u00e1ci\u00f3s r\u00e9sz {0} friss\u00edtve",
"MessageApplicationUpdated": "Emby Szerver friss\u00edtve",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
+ "AuthenticationSucceededWithUserName": "{0} sikeresen azonos\u00edtva",
"UserOfflineFromDevice": "{0} kijelentkezett innen {1}",
"DeviceOfflineWithName": "{0} kijelentkezett",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} elkezdte j\u00e1tszani a k\u00f6vetkez\u0151t {1}",
+ "UserStoppedPlayingItemWithValues": "{0} befejezte a k\u00f6vetkez\u0151t {1}",
"NotificationOptionPluginError": "B\u0151v\u00edtm\u00e9ny hiba",
- "NotificationOptionApplicationUpdateAvailable": "Friss\u00edt\u00e9s el\u00e9rhet\u0151",
+ "NotificationOptionApplicationUpdateAvailable": "Program friss\u00edt\u00e9s el\u00e9rhet\u0151",
"NotificationOptionApplicationUpdateInstalled": "Program friss\u00edt\u00e9s telep\u00edtve",
"NotificationOptionPluginUpdateInstalled": "B\u0151v\u00edtm\u00e9ny friss\u00edt\u00e9s telep\u00edtve",
"NotificationOptionPluginInstalled": "B\u0151v\u00edtm\u00e9ny telep\u00edtve",
@@ -84,8 +93,8 @@
"UserLockedOutWithName": "User {0} has been locked out",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"Sync": "Szinkroniz\u00e1l",
- "User": "User",
- "System": "System",
- "Application": "Application",
- "Plugin": "Plugin"
+ "User": "Felhaszn\u00e1l\u00f3",
+ "System": "Rendszer",
+ "Application": "Program",
+ "Plugin": "B\u0151v\u00edtm\u00e9ny"
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 42605acdb..f1ce264cf 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Caricamenti Fotocamera",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Pi\u00f9 recenti",
"ValueSpecialEpisodeName": "Speciale - {0}",
"Inherit": "Eredita",
@@ -24,10 +32,11 @@
"Channels": "Canali",
"Movies": "Film",
"Albums": "Album",
+ "NameSeasonUnknown": "Stagione sconosciuto",
"Artists": "Artisti",
"Folders": "Cartelle",
"Songs": "Canzoni",
- "TvShows": "TV Shows",
+ "TvShows": "Serie TV",
"Shows": "Programmi",
"Genres": "Generi",
"NameSeasonNumber": "Stagione {0}",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "L'utente {0} \u00e8 stato creato",
"UserPasswordChangedWithName": "La password \u00e8 stata cambiata per l'utente {0}",
"UserDeletedWithName": "L'utente {0} \u00e8 stato rimosso",
- "UserConfigurationUpdatedWithName": "La configurazione utente \u00e8 stata aggiornata per {0}",
+ "UserPolicyUpdatedWithName": "La politica dell'utente \u00e8 stata aggiornata per {0}",
"MessageServerConfigurationUpdated": "La configurazione del server \u00e8 stata aggiornata",
"MessageNamedServerConfigurationUpdatedWithValue": "La sezione {0} della configurazione server \u00e8 stata aggiornata",
"MessageApplicationUpdated": "Il Server Emby \u00e8 stato aggiornato",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index a991fe363..ef487aa8c 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "\u041a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u0436\u04af\u043a\u0442\u0435\u043b\u0433\u0435\u043d\u0434\u0435\u0440",
+ "ValueHasBeenAddedToLibrary": "{0} (\u0442\u0430\u0441\u044b\u0493\u044b\u0448\u0445\u0430\u043d\u0430\u0493\u0430 \u04af\u0441\u0442\u0435\u043b\u0456\u043d\u0434\u0456)",
+ "NameInstallFailed": "{0} \u043e\u0440\u043d\u0430\u0442\u044b\u043b\u0443\u044b \u0441\u04d9\u0442\u0441\u0456\u0437",
+ "CameraImageUploadedFrom": "\u0416\u0430\u04a3\u0430 \u0441\u0443\u0440\u0435\u0442 {0} \u043a\u0430\u043c\u0435\u0440\u0430\u0441\u044b\u043d\u0430\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u044b\u043d\u0434\u044b",
+ "ServerNameNeedsToBeRestarted": "{0} \u049b\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443 \u049b\u0430\u0436\u0435\u0442",
+ "NewVersionIsAvailable": "\u0416\u0430\u04a3\u0430 Emby Server \u043d\u04b1\u0441\u049b\u0430\u0441\u044b \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443\u0493\u0430 \u049b\u043e\u043b\u0436\u0435\u0442\u0456\u043c\u0434\u0456.",
+ "MessageApplicationUpdatedTo": "Emby Server {0} \u04af\u0448\u0456\u043d \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u0415\u04a3 \u043a\u0435\u0439\u0456\u043d\u0433\u0456",
"ValueSpecialEpisodeName": "\u0410\u0440\u043d\u0430\u0439\u044b - {0}",
"Inherit": "\u041c\u04b1\u0440\u0430\u0493\u0430 \u0438\u0435\u043b\u0435\u043d\u0443",
@@ -24,6 +32,7 @@
"Channels": "\u0410\u0440\u043d\u0430\u043b\u0430\u0440",
"Movies": "\u0424\u0438\u043b\u044c\u043c\u0434\u0435\u0440",
"Albums": "\u0410\u043b\u044c\u0431\u043e\u043c\u0434\u0430\u0440",
+ "NameSeasonUnknown": "\u0411\u0435\u043b\u0433\u0456\u0441\u0456\u0437 \u043c\u0430\u0443\u0441\u044b\u043c",
"Artists": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440",
"Folders": "\u049a\u0430\u043b\u0442\u0430\u043b\u0430\u0440",
"Songs": "\u04d8\u0443\u0435\u043d\u0434\u0435\u0440",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u0436\u0430\u0441\u0430\u043b\u0493\u0430\u043d",
"UserPasswordChangedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u04af\u0448\u0456\u043d \u049b\u04b1\u043f\u0438\u044f \u0441\u04e9\u0437 \u04e9\u0437\u0433\u0435\u0440\u0442\u0456\u043b\u0434\u0456",
"UserDeletedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u0436\u043e\u0439\u044b\u043b\u0493\u0430\u043d",
- "UserConfigurationUpdatedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u04af\u0448\u0456\u043d \u0442\u0435\u04a3\u0448\u0435\u043b\u0456\u043c \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b",
+ "UserPolicyUpdatedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u04af\u0448\u0456\u043d \u0441\u0430\u044f\u0441\u0430\u0442\u0442\u0430\u0440\u044b \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b",
"MessageServerConfigurationUpdated": "\u0421\u0435\u0440\u0432\u0435\u0440 \u0442\u0435\u04a3\u0448\u0435\u043b\u0456\u043c\u0456 \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b",
"MessageNamedServerConfigurationUpdatedWithValue": "\u0421\u0435\u0440\u0432\u0435\u0440 \u0442\u0435\u04a3\u0448\u0435\u043b\u0456\u043c\u0456 ({0} \u0431\u04e9\u043b\u0456\u043c\u0456) \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b",
"MessageApplicationUpdated": "Emby Server \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b.",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} \u0442\u04af\u043f\u043d\u04b1\u0441\u049b\u0430\u043b\u044b\u0493\u044b\u043d \u0440\u0430\u0441\u0442\u0430\u043b\u0443\u044b \u0441\u04d9\u0442\u0442\u0456",
"UserOfflineFromDevice": "{0} - {1} \u0442\u0430\u0440\u0430\u043f\u044b\u043d\u0430\u043d \u0430\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u0493\u0430\u043d",
"DeviceOfflineWithName": "{0} \u0430\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u0493\u0430\u043d",
- "UserStartedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d \u0431\u0430\u0441\u0442\u0430\u0434\u044b",
- "UserStoppedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d \u0442\u043e\u049b\u0442\u0430\u0442\u0442\u044b",
+ "UserStartedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d {2} \u0431\u0430\u0441\u0442\u0430\u0434\u044b",
+ "UserStoppedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d {2} \u0442\u043e\u049b\u0442\u0430\u0442\u0442\u044b",
"NotificationOptionPluginError": "\u041f\u043b\u0430\u0433\u0438\u043d \u0441\u04d9\u0442\u0441\u0456\u0437\u0434\u0456\u0433\u0456",
"NotificationOptionApplicationUpdateAvailable": "\u049a\u043e\u043b\u0434\u0430\u043d\u0431\u0430 \u0436\u0430\u04a3\u0430\u0440\u0442\u0443\u044b \u049b\u043e\u043b\u0436\u0435\u0442\u0456\u043c\u0434\u0456",
"NotificationOptionApplicationUpdateInstalled": "\u049a\u043e\u043b\u0434\u0430\u043d\u0431\u0430 \u0436\u0430\u04a3\u0430\u0440\u0442\u0443\u044b \u043e\u0440\u043d\u0430\u0442\u044b\u043b\u0434\u044b",
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 0f99c8432..30709dde0 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index 9e1fede1d..c71d2424a 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Ypatinga - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Filmai",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index c48042d9a..17d9c80a0 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 5cd9894be..3ceeb9ce9 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Siste",
"ValueSpecialEpisodeName": "Spesial - {0}",
"Inherit": "Arve",
@@ -24,6 +32,7 @@
"Channels": "Kanaler",
"Movies": "Filmer",
"Albums": "Album",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artister",
"Folders": "Mapper",
"Songs": "Sanger",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Bruker {0} er opprettet",
"UserPasswordChangedWithName": "Passordet for {0} er oppdatert",
"UserDeletedWithName": "Bruker {0} har blitt slettet",
- "UserConfigurationUpdatedWithName": "Brukerkonfigurasjon har blitt oppdatert for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server konfigurasjon er oppdatert",
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurasjon seksjon {0} har blitt oppdatert",
"MessageApplicationUpdated": "Emby server har blitt oppdatert",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index d79fcf747..a206ed92a 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Nieuwste",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"Inherit": "Overerven",
@@ -7,27 +15,28 @@
"Games": "Spellen",
"Photos": "Foto's",
"MixedContent": "Gemengde inhoud",
- "MusicVideos": "Muziek video's",
+ "MusicVideos": "Muziekvideo's",
"HomeVideos": "Thuis video's",
"Playlists": "Afspeellijsten",
- "HeaderRecordingGroups": "Opname groepen",
+ "HeaderRecordingGroups": "Opnamegroepen",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteSongs": "Favoriete titels",
"HeaderAlbumArtists": "Album artiesten",
"HeaderFavoriteAlbums": "Favoriete albums",
- "HeaderFavoriteEpisodes": "Favoriete Afleveringen",
- "HeaderFavoriteShows": "Favoriete Shows",
+ "HeaderFavoriteEpisodes": "Favoriete afleveringen",
+ "HeaderFavoriteShows": "Favoriete shows",
"HeaderNextUp": "Volgende",
"Favorites": "Favorieten",
"Collections": "Collecties",
"Channels": "Kanalen",
"Movies": "Films",
"Albums": "Albums",
+ "NameSeasonUnknown": "Seizoen onbekend",
"Artists": "Artiesten",
"Folders": "Mappen",
"Songs": "Titels",
- "TvShows": "TV Shows",
+ "TvShows": "TV-series",
"Shows": "Series",
"Genres": "Genres",
"NameSeasonNumber": "Seizoen {0}",
@@ -42,7 +51,7 @@
"PluginInstalledWithName": "{0} is ge\u00efnstalleerd",
"StartupEmbyServerIsLoading": "Emby Server is aan het laden, probeer het later opnieuw.",
"PluginUpdatedWithName": "{0} is bijgewerkt",
- "PluginUninstalledWithName": "{0} is gede\u00efnstalleerd",
+ "PluginUninstalledWithName": "{0} is verwijderd",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
"LabelIpAddressValue": "IP adres: {0}",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Gebruiker {0} is aangemaakt",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
"UserDeletedWithName": "Gebruiker {0} is verwijderd",
- "UserConfigurationUpdatedWithName": "Gebruikersinstellingen voor {0} zijn bijgewerkt",
+ "UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
"MessageServerConfigurationUpdated": "Server configuratie is bijgewerkt",
"MessageNamedServerConfigurationUpdatedWithValue": "Sectie {0} van de server configuratie is bijgewerkt",
"MessageApplicationUpdated": "Emby Server is bijgewerkt",
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index 896df24dd..065b2280c 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -1,5 +1,13 @@
{
- "Latest": "Ostatnio dodane do",
+ "HeaderCameraUploads": "Przekazane obrazy",
+ "ValueHasBeenAddedToLibrary": "{0} zosta\u0142 dodany to biblioteki medi\u00f3w",
+ "NameInstallFailed": "Instalacja {0} nieudana.",
+ "CameraImageUploadedFrom": "Nowy obraz zosta\u0142 przekazany z {0}",
+ "ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia",
+ "NewVersionIsAvailable": "Nowa wersja serwera Emby jest dost\u0119pna do pobrania.",
+ "MessageApplicationUpdatedTo": "Serwer Emby zosta\u0142 zaktualizowany do wersji {0}",
+ "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napis\u00f3w z {0} dla {1}",
+ "Latest": "Ostatnio dodane",
"ValueSpecialEpisodeName": "Specjalne - {0}",
"Inherit": "Dziedzicz",
"Books": "Ksi\u0105\u017cki",
@@ -11,7 +19,7 @@
"HomeVideos": "Nagrania prywatne",
"Playlists": "Listy odtwarzania",
"HeaderRecordingGroups": "Grupy nagra\u0144",
- "HeaderContinueWatching": "Kontynuuj ogl\u0105danie",
+ "HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteArtists": "Wykonawcy ulubieni",
"HeaderFavoriteSongs": "Utwory ulubione",
"HeaderAlbumArtists": "Wykonawcy album\u00f3w",
@@ -24,6 +32,7 @@
"Channels": "Kana\u0142y",
"Movies": "Filmy",
"Albums": "Albumy",
+ "NameSeasonUnknown": "Sezon nieznany",
"Artists": "Wykonawcy",
"Folders": "Foldery",
"Songs": "Utwory",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "U\u017cytkownik {0} zosta\u0142 utworzony",
"UserPasswordChangedWithName": "Has\u0142o u\u017cytkownika {0} zosta\u0142o zmienione",
"UserDeletedWithName": "U\u017cytkownik {0} zosta\u0142 usuni\u0119ty",
- "UserConfigurationUpdatedWithName": "Konfiguracja u\u017cytkownika {0} zosta\u0142a zaktualizowana",
+ "UserPolicyUpdatedWithName": "Zmieniono zasady u\u017cytkowania dla {0}",
"MessageServerConfigurationUpdated": "Konfiguracja serwera zosta\u0142a zaktualizowana",
"MessageNamedServerConfigurationUpdatedWithValue": "Sekcja {0} konfiguracji serwera zosta\u0142a zaktualizowana",
"MessageApplicationUpdated": "Serwer Emby zosta\u0142 zaktualizowany",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} zosta\u0142 pomy\u015blnie uwierzytelniony",
"UserOfflineFromDevice": "{0} z {1} zosta\u0142 roz\u0142\u0105czony",
"DeviceOfflineWithName": "{0} zosta\u0142 roz\u0142\u0105czony",
- "UserStartedPlayingItemWithValues": "{0} rozpocz\u0105\u0142 odtwarzanie {1}",
- "UserStoppedPlayingItemWithValues": "{0} zatrzyma\u0142 odtwarzanie {1}",
+ "UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}",
+ "UserStoppedPlayingItemWithValues": "{0} zako\u0144czy\u0142 odtwarzanie {1} na {2}",
"NotificationOptionPluginError": "Awaria wtyczki",
"NotificationOptionApplicationUpdateAvailable": "Dost\u0119pna aktualizacja aplikacji",
"NotificationOptionApplicationUpdateInstalled": "Zainstalowano aktualizacj\u0119 aplikacji",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index e0a375170..6e948f507 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Uploads da C\u00e2mera",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Recente",
"ValueSpecialEpisodeName": "Especial - {0}",
"Inherit": "Herdar",
@@ -24,6 +32,7 @@
"Channels": "Canais",
"Movies": "Filmes",
"Albums": "\u00c1lbuns",
+ "NameSeasonUnknown": "Temporada Desconhecida",
"Artists": "Artistas",
"Folders": "Pastas",
"Songs": "M\u00fasicas",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "O usu\u00e1rio {0} foi criado",
"UserPasswordChangedWithName": "A senha foi alterada para o usu\u00e1rio {0}",
"UserDeletedWithName": "O usu\u00e1rio {0} foi exclu\u00eddo",
- "UserConfigurationUpdatedWithName": "A configura\u00e7\u00e3o do usu\u00e1rio foi atualizada para {0}",
+ "UserPolicyUpdatedWithName": "A pol\u00edtica de usu\u00e1rio foi atualizada para {0}",
"MessageServerConfigurationUpdated": "A configura\u00e7\u00e3o do servidor foi atualizada",
"MessageNamedServerConfigurationUpdatedWithValue": "A se\u00e7\u00e3o {0} da configura\u00e7\u00e3o do servidor foi atualizada",
"MessageApplicationUpdated": "O servidor Emby foi atualizado",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index ac20fa1e5..71d7e142e 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Especial - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 12345ca14..f842f8d2d 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "\u041a\u0430\u043c\u0435\u0440\u044b",
+ "ValueHasBeenAddedToLibrary": "{0} (\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0443)",
+ "NameInstallFailed": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 {0} \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u0430",
+ "CameraImageUploadedFrom": "\u041d\u043e\u0432\u043e\u0435 \u0444\u043e\u0442\u043e \u0431\u044b\u043b\u043e \u0432\u044b\u043b\u043e\u0436\u0435\u043d\u043e \u0441 {0}",
+ "ServerNameNeedsToBeRestarted": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a {0}",
+ "NewVersionIsAvailable": "\u0418\u043c\u0435\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Emby Server",
+ "MessageApplicationUpdatedTo": "Emby Server \u0431\u044b\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d \u0434\u043e {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u041d\u043e\u0432\u0435\u0439\u0448\u0435\u0435",
"ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u044d\u043f\u0438\u0437\u043e\u0434 - {0}",
"Inherit": "\u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c\u043e\u0435",
@@ -24,6 +32,7 @@
"Channels": "\u041a\u0430\u043d\u0430\u043b\u044b",
"Movies": "\u041a\u0438\u043d\u043e",
"Albums": "\u0410\u043b\u044c\u0431\u043e\u043c\u044b",
+ "NameSeasonUnknown": "\u0421\u0435\u0437\u043e\u043d \u043d\u0435\u043e\u043f\u043e\u0437\u043d\u0430\u043d",
"Artists": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438",
"Folders": "\u041f\u0430\u043f\u043a\u0438",
"Songs": "\u041a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c {0} \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d",
"UserPasswordChangedWithName": "\u041f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437-\u043b\u044f {0} \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d",
"UserDeletedWithName": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c {0} \u0431\u044b\u043b \u0443\u0434\u0430\u043b\u0451\u043d",
- "UserConfigurationUpdatedWithName": "\u041a\u043e\u043d\u0444\u0438\u0433-\u0438\u044f \u043f\u043e\u043b\u044c\u0437-\u043b\u044f {0} \u0431\u044b\u043b\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430",
+ "UserPolicyUpdatedWithName": "\u041f\u043e\u043b\u044c\u0437-\u0438\u0435 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 {0} \u0431\u044b\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b",
"MessageServerConfigurationUpdated": "\u041a\u043e\u043d\u0444\u0438\u0433-\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0431\u044b\u043b\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430",
"MessageNamedServerConfigurationUpdatedWithValue": "\u041a\u043e\u043d\u0444\u0438\u0433-\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0440\u0430\u0437\u0434\u0435\u043b {0}) \u0431\u044b\u043b\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430",
"MessageApplicationUpdated": "Emby Server \u0431\u044b\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} - \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u0430",
"UserOfflineFromDevice": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0441 {1} \u0440\u0430\u0437\u044a-\u043d\u043e",
"DeviceOfflineWithName": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0440\u0430\u0437\u044a-\u043d\u043e",
- "UserStartedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u0437\u0430\u043f-\u043d\u043e",
- "UserStoppedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u043e\u0441\u0442-\u043d\u043e",
+ "UserStartedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u043d\u0430 {2}",
+ "UserStoppedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u043e\u0441\u0442-\u043d\u043e \u043d\u0430 {2}",
"NotificationOptionPluginError": "\u0421\u0431\u043e\u0439 \u043f\u043b\u0430\u0433\u0438\u043d\u0430",
"NotificationOptionApplicationUpdateAvailable": "\u0418\u043c\u0435\u0435\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
"NotificationOptionApplicationUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e",
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index f09eb1506..68d9222ff 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Najnov\u0161ie",
"ValueSpecialEpisodeName": "\u0160peci\u00e1l - {0}",
"Inherit": "Zdedi\u0165",
@@ -24,6 +32,7 @@
"Channels": "Kan\u00e1ly",
"Movies": "Filmy",
"Albums": "Albumy",
+ "NameSeasonUnknown": "Nezn\u00e1ma sez\u00f3na",
"Artists": "Umelci",
"Folders": "Prie\u010dinky",
"Songs": "Skladby",
@@ -33,7 +42,7 @@
"NameSeasonNumber": "Sez\u00f3na {0}",
"AppDeviceValues": "Aplik\u00e1cia: {0}, Zariadenie: {1}",
"UserDownloadingItemWithValues": "{0} s\u0165ahuje {1}",
- "HeaderLiveTV": "Live TV",
+ "HeaderLiveTV": "\u017div\u00e1 TV",
"ChapterNameValue": "Kapitola {0}",
"ScheduledTaskFailedWithName": "{0} zlyhalo",
"LabelRunningTimeValue": "D\u013a\u017eka: {0}",
@@ -48,12 +57,12 @@
"LabelIpAddressValue": "IP adresa: {0}",
"DeviceOnlineWithName": "{0} je pripojen\u00fd",
"UserOnlineFromDevice": "{0} je online z {1}",
- "ProviderValue": "Provider: {0}",
+ "ProviderValue": "Poskytovate\u013e: {0}",
"SubtitlesDownloadedForItem": "Titulky pre {0} stiahnut\u00e9",
"UserCreatedWithName": "Pou\u017e\u00edvate\u013e {0} bol vytvoren\u00fd",
"UserPasswordChangedWithName": "Heslo pou\u017e\u00edvate\u013ea {0} zmenen\u00e9",
"UserDeletedWithName": "Pou\u017e\u00edvate\u013e {0} bol vymazan\u00fd",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Konfigur\u00e1cia servera aktualizovan\u00e1",
"MessageNamedServerConfigurationUpdatedWithValue": "Sekcia {0} konfigur\u00e1cie servera bola aktualizovan\u00e1",
"MessageApplicationUpdated": "Emby Server bol aktualizovan\u00fd",
@@ -63,12 +72,12 @@
"DeviceOfflineWithName": "{0} je odpojen\u00fd",
"UserStartedPlayingItemWithValues": "{0} spustil prehr\u00e1vanie {1}",
"UserStoppedPlayingItemWithValues": "{0} zastavil prehr\u00e1vanie {1}",
- "NotificationOptionPluginError": "Plugin failure",
+ "NotificationOptionPluginError": "Chyba roz\u0161\u00edrenia",
"NotificationOptionApplicationUpdateAvailable": "Je dostupn\u00e1 aktualiz\u00e1cia aplik\u00e1cie",
"NotificationOptionApplicationUpdateInstalled": "Aktualiz\u00e1cia aplik\u00e1cie nain\u0161talovan\u00e1",
- "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
- "NotificationOptionPluginInstalled": "Plugin installed",
- "NotificationOptionPluginUninstalled": "Plugin uninstalled",
+ "NotificationOptionPluginUpdateInstalled": "Aktualiz\u00e1cia roz\u0161\u00edrenia nain\u0161talovan\u00e1",
+ "NotificationOptionPluginInstalled": "Roz\u0161\u00edrenie nain\u0161talovan\u00e9",
+ "NotificationOptionPluginUninstalled": "Roz\u0161\u00edrenie odin\u0161talovan\u00e9",
"NotificationOptionVideoPlayback": "Spusten\u00e9 prehr\u00e1vanie videa",
"NotificationOptionAudioPlayback": "Spusten\u00e9 prehr\u00e1vanie audia",
"NotificationOptionGamePlayback": "Game playback started",
@@ -78,7 +87,7 @@
"NotificationOptionTaskFailed": "Napl\u00e1novan\u00e1 \u00faloha zlyhala",
"NotificationOptionInstallationFailed": "Chyba in\u0161tal\u00e1cie",
"NotificationOptionNewLibraryContent": "Pridan\u00fd nov\u00fd obsah",
- "NotificationOptionCameraImageUploaded": "Camera image uploaded",
+ "NotificationOptionCameraImageUploaded": "Nahran\u00fd obr\u00e1zok z fotoapar\u00e1tu",
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionServerRestartRequired": "Vy\u017eaduje sa re\u0161tart servera",
"UserLockedOutWithName": "User {0} has been locked out",
@@ -87,5 +96,5 @@
"User": "Pou\u017e\u00edvate\u013e",
"System": "Syst\u00e9m",
"Application": "Aplik\u00e1cia",
- "Plugin": "Plugin"
+ "Plugin": "Roz\u0161\u00edrenie"
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 27561a890..ebee7b692 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index ed1867024..4323b38ca 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Senaste",
"ValueSpecialEpisodeName": "Specialavsnitt - {0}",
"Inherit": "\u00c4rv",
@@ -24,6 +32,7 @@
"Channels": "Kanaler",
"Movies": "Filmer",
"Albums": "Album",
+ "NameSeasonUnknown": "Ok\u00e4nd s\u00e4song",
"Artists": "Artister",
"Folders": "Mappar",
"Songs": "L\u00e5tar",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "Anv\u00e4ndaren {0} har skapats",
"UserPasswordChangedWithName": "L\u00f6senordet f\u00f6r {0} har \u00e4ndrats",
"UserDeletedWithName": "Anv\u00e4ndaren {0} har tagits bort",
- "UserConfigurationUpdatedWithName": "Anv\u00e4ndarinst\u00e4llningarna f\u00f6r {0} har uppdaterats",
+ "UserPolicyUpdatedWithName": "Anv\u00e4ndarpolicyn har uppdaterats f\u00f6r {0}",
"MessageServerConfigurationUpdated": "Server konfigurationen har uppdaterats",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverinst\u00e4llningarna {0} har uppdaterats",
"MessageApplicationUpdated": "Emby Server har uppdaterats",
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 71af4110d..9d6922df0 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 0f248f3cd..6b7bd2f71 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "\u76f8\u673a\u4e0a\u4f20",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "\u6700\u65b0",
"ValueSpecialEpisodeName": "\u7279\u5178 - {0}",
"Inherit": "\u7ee7\u627f",
@@ -24,10 +32,11 @@
"Channels": "\u9891\u9053",
"Movies": "\u7535\u5f71",
"Albums": "\u4e13\u8f91",
+ "NameSeasonUnknown": "\u672a\u77e5\u5b63",
"Artists": "\u827a\u672f\u5bb6",
"Folders": "\u6587\u4ef6\u5939",
"Songs": "\u6b4c\u66f2",
- "TvShows": "TV Shows",
+ "TvShows": "\u7535\u89c6\u8282\u76ee",
"Shows": "\u8282\u76ee",
"Genres": "\u98ce\u683c",
"NameSeasonNumber": "\u5b63 {0}",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "\u7528\u6237 {0} \u5df2\u521b\u5efa",
"UserPasswordChangedWithName": "\u5df2\u4e3a\u7528\u6237 {0} \u66f4\u6539\u5bc6\u7801",
"UserDeletedWithName": "\u7528\u6237 {0} \u5df2\u5220\u9664",
- "UserConfigurationUpdatedWithName": "{0} \u7684\u7528\u6237\u914d\u7f6e\u5df2\u66f4\u65b0",
+ "UserPolicyUpdatedWithName": "\u7528\u6237\u534f\u8bae\u5df2\u7ecf\u88ab\u66f4\u65b0\u4e3a {0}",
"MessageServerConfigurationUpdated": "\u670d\u52a1\u5668\u914d\u7f6e\u5df2\u66f4\u65b0",
"MessageNamedServerConfigurationUpdatedWithValue": "\u670d\u52a1\u5668\u914d\u7f6e {0} \u90e8\u5206\u5df2\u66f4\u65b0",
"MessageApplicationUpdated": "Emby \u670d\u52a1\u5668\u5df2\u66f4\u65b0",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index b60edb176..1abfe7e98 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -1,4 +1,12 @@
{
+ "HeaderCameraUploads": "Camera Uploads",
+ "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "NameInstallFailed": "{0} installation failed",
+ "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "NewVersionIsAvailable": "A new version of Emby Server is available for download.",
+ "MessageApplicationUpdatedTo": "Emby Server has been updated to {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Latest": "Latest",
"ValueSpecialEpisodeName": "Special - {0}",
"Inherit": "Inherit",
@@ -24,6 +32,7 @@
"Channels": "Channels",
"Movies": "Movies",
"Albums": "Albums",
+ "NameSeasonUnknown": "Season Unknown",
"Artists": "Artists",
"Folders": "Folders",
"Songs": "Songs",
@@ -53,7 +62,7 @@
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
- "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+ "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Emby Server has been updated",
@@ -61,8 +70,8 @@
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
- "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 6d271c0e1..71a4ca824 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -30,8 +30,8 @@ namespace Emby.Server.Implementations.Localization
/// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- private readonly ConcurrentDictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
- new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
+ new Dictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
@@ -96,6 +96,61 @@ namespace Emby.Server.Implementations.Localization
{
LoadRatings(file);
}
+
+ LoadAdditionalRatings();
+ }
+
+ private void LoadAdditionalRatings()
+ {
+ LoadRatings("au", new[] {
+
+ new ParentalRating("AU-G", 1),
+ new ParentalRating("AU-PG", 5),
+ new ParentalRating("AU-M", 6),
+ new ParentalRating("AU-MA15+", 7),
+ new ParentalRating("AU-M15+", 8),
+ new ParentalRating("AU-R18+", 9),
+ new ParentalRating("AU-X18+", 10),
+ new ParentalRating("AU-RC", 11)
+ });
+
+ LoadRatings("be", new[] {
+
+ new ParentalRating("BE-AL", 1),
+ new ParentalRating("BE-MG6", 2),
+ new ParentalRating("BE-6", 3),
+ new ParentalRating("BE-9", 5),
+ new ParentalRating("BE-12", 6),
+ new ParentalRating("BE-16", 8)
+ });
+
+ LoadRatings("de", new[] {
+
+ new ParentalRating("DE-0", 1),
+ new ParentalRating("FSK-0", 1),
+ new ParentalRating("DE-6", 5),
+ new ParentalRating("FSK-6", 5),
+ new ParentalRating("DE-12", 7),
+ new ParentalRating("FSK-12", 7),
+ new ParentalRating("DE-16", 8),
+ new ParentalRating("FSK-16", 8),
+ new ParentalRating("DE-18", 9),
+ new ParentalRating("FSK-18", 9)
+ });
+
+ LoadRatings("ru", new [] {
+
+ new ParentalRating("RU-0+", 1),
+ new ParentalRating("RU-6+", 3),
+ new ParentalRating("RU-12+", 7),
+ new ParentalRating("RU-16+", 9),
+ new ParentalRating("RU-18+", 10)
+ });
+ }
+
+ private void LoadRatings(string country, ParentalRating[] ratings)
+ {
+ _allParentalRatings[country] = ratings.ToDictionary(i => i.Name);
}
private List<string> GetRatingsFiles(string directory)
@@ -161,11 +216,17 @@ namespace Emby.Server.Implementations.Localization
if (parts.Length == 5)
{
+ var threeletterNames = new List<string> { parts[0] };
+ if (!string.IsNullOrWhiteSpace(parts[1]))
+ {
+ threeletterNames.Add(parts[1]);
+ }
+
list.Add(new CultureDto
{
DisplayName = parts[3],
Name = parts[3],
- ThreeLetterISOLanguageName = parts[0],
+ ThreeLetterISOLanguageNames = threeletterNames.ToArray(),
TwoLetterISOLanguageName = parts[2]
});
}
@@ -176,7 +237,7 @@ namespace Emby.Server.Implementations.Localization
result = list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
!string.IsNullOrWhiteSpace(i.DisplayName) &&
- !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
+ i.ThreeLetterISOLanguageNames.Length > 0 &&
!string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray();
_cultures = result;
@@ -184,19 +245,25 @@ namespace Emby.Server.Implementations.Localization
return result;
}
+ public CultureDto FindLanguageInfo(string language)
+ {
+ return GetCultures()
+ .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) ||
+ i.ThreeLetterISOLanguageNames.Contains(language, StringComparer.OrdinalIgnoreCase) ||
+ string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
+ }
+
/// <summary>
/// Gets the countries.
/// </summary>
/// <returns>IEnumerable{CountryInfo}.</returns>
public CountryInfo[] GetCountries()
{
- var type = GetType();
- var path = type.Namespace + ".countries.json";
+ // ToDo: DeserializeFromStream seems broken in this case
+ string jsonCountries = "[{\"Name\":\"AF\",\"DisplayName\":\"Afghanistan\",\"TwoLetterISORegionName\":\"AF\",\"ThreeLetterISORegionName\":\"AFG\"},{\"Name\":\"AL\",\"DisplayName\":\"Albania\",\"TwoLetterISORegionName\":\"AL\",\"ThreeLetterISORegionName\":\"ALB\"},{\"Name\":\"DZ\",\"DisplayName\":\"Algeria\",\"TwoLetterISORegionName\":\"DZ\",\"ThreeLetterISORegionName\":\"DZA\"},{\"Name\":\"AR\",\"DisplayName\":\"Argentina\",\"TwoLetterISORegionName\":\"AR\",\"ThreeLetterISORegionName\":\"ARG\"},{\"Name\":\"AM\",\"DisplayName\":\"Armenia\",\"TwoLetterISORegionName\":\"AM\",\"ThreeLetterISORegionName\":\"ARM\"},{\"Name\":\"AU\",\"DisplayName\":\"Australia\",\"TwoLetterISORegionName\":\"AU\",\"ThreeLetterISORegionName\":\"AUS\"},{\"Name\":\"AT\",\"DisplayName\":\"Austria\",\"TwoLetterISORegionName\":\"AT\",\"ThreeLetterISORegionName\":\"AUT\"},{\"Name\":\"AZ\",\"DisplayName\":\"Azerbaijan\",\"TwoLetterISORegionName\":\"AZ\",\"ThreeLetterISORegionName\":\"AZE\"},{\"Name\":\"BH\",\"DisplayName\":\"Bahrain\",\"TwoLetterISORegionName\":\"BH\",\"ThreeLetterISORegionName\":\"BHR\"},{\"Name\":\"BD\",\"DisplayName\":\"Bangladesh\",\"TwoLetterISORegionName\":\"BD\",\"ThreeLetterISORegionName\":\"BGD\"},{\"Name\":\"BY\",\"DisplayName\":\"Belarus\",\"TwoLetterISORegionName\":\"BY\",\"ThreeLetterISORegionName\":\"BLR\"},{\"Name\":\"BE\",\"DisplayName\":\"Belgium\",\"TwoLetterISORegionName\":\"BE\",\"ThreeLetterISORegionName\":\"BEL\"},{\"Name\":\"BZ\",\"DisplayName\":\"Belize\",\"TwoLetterISORegionName\":\"BZ\",\"ThreeLetterISORegionName\":\"BLZ\"},{\"Name\":\"VE\",\"DisplayName\":\"Bolivarian Republic of Venezuela\",\"TwoLetterISORegionName\":\"VE\",\"ThreeLetterISORegionName\":\"VEN\"},{\"Name\":\"BO\",\"DisplayName\":\"Bolivia\",\"TwoLetterISORegionName\":\"BO\",\"ThreeLetterISORegionName\":\"BOL\"},{\"Name\":\"BA\",\"DisplayName\":\"Bosnia and Herzegovina\",\"TwoLetterISORegionName\":\"BA\",\"ThreeLetterISORegionName\":\"BIH\"},{\"Name\":\"BW\",\"DisplayName\":\"Botswana\",\"TwoLetterISORegionName\":\"BW\",\"ThreeLetterISORegionName\":\"BWA\"},{\"Name\":\"BR\",\"DisplayName\":\"Brazil\",\"TwoLetterISORegionName\":\"BR\",\"ThreeLetterISORegionName\":\"BRA\"},{\"Name\":\"BN\",\"DisplayName\":\"Brunei Darussalam\",\"TwoLetterISORegionName\":\"BN\",\"ThreeLetterISORegionName\":\"BRN\"},{\"Name\":\"BG\",\"DisplayName\":\"Bulgaria\",\"TwoLetterISORegionName\":\"BG\",\"ThreeLetterISORegionName\":\"BGR\"},{\"Name\":\"KH\",\"DisplayName\":\"Cambodia\",\"TwoLetterISORegionName\":\"KH\",\"ThreeLetterISORegionName\":\"KHM\"},{\"Name\":\"CM\",\"DisplayName\":\"Cameroon\",\"TwoLetterISORegionName\":\"CM\",\"ThreeLetterISORegionName\":\"CMR\"},{\"Name\":\"CA\",\"DisplayName\":\"Canada\",\"TwoLetterISORegionName\":\"CA\",\"ThreeLetterISORegionName\":\"CAN\"},{\"Name\":\"029\",\"DisplayName\":\"Caribbean\",\"TwoLetterISORegionName\":\"029\",\"ThreeLetterISORegionName\":\"029\"},{\"Name\":\"CL\",\"DisplayName\":\"Chile\",\"TwoLetterISORegionName\":\"CL\",\"ThreeLetterISORegionName\":\"CHL\"},{\"Name\":\"CO\",\"DisplayName\":\"Colombia\",\"TwoLetterISORegionName\":\"CO\",\"ThreeLetterISORegionName\":\"COL\"},{\"Name\":\"CD\",\"DisplayName\":\"Congo [DRC]\",\"TwoLetterISORegionName\":\"CD\",\"ThreeLetterISORegionName\":\"COD\"},{\"Name\":\"CR\",\"DisplayName\":\"Costa Rica\",\"TwoLetterISORegionName\":\"CR\",\"ThreeLetterISORegionName\":\"CRI\"},{\"Name\":\"HR\",\"DisplayName\":\"Croatia\",\"TwoLetterISORegionName\":\"HR\",\"ThreeLetterISORegionName\":\"HRV\"},{\"Name\":\"CZ\",\"DisplayName\":\"Czech Republic\",\"TwoLetterISORegionName\":\"CZ\",\"ThreeLetterISORegionName\":\"CZE\"},{\"Name\":\"DK\",\"DisplayName\":\"Denmark\",\"TwoLetterISORegionName\":\"DK\",\"ThreeLetterISORegionName\":\"DNK\"},{\"Name\":\"DO\",\"DisplayName\":\"Dominican Republic\",\"TwoLetterISORegionName\":\"DO\",\"ThreeLetterISORegionName\":\"DOM\"},{\"Name\":\"EC\",\"DisplayName\":\"Ecuador\",\"TwoLetterISORegionName\":\"EC\",\"ThreeLetterISORegionName\":\"ECU\"},{\"Name\":\"EG\",\"DisplayName\":\"Egypt\",\"TwoLetterISORegionName\":\"EG\",\"ThreeLetterISORegionName\":\"EGY\"},{\"Name\":\"SV\",\"DisplayName\":\"El Salvador\",\"TwoLetterISORegionName\":\"SV\",\"ThreeLetterISORegionName\":\"SLV\"},{\"Name\":\"ER\",\"DisplayName\":\"Eritrea\",\"TwoLetterISORegionName\":\"ER\",\"ThreeLetterISORegionName\":\"ERI\"},{\"Name\":\"EE\",\"DisplayName\":\"Estonia\",\"TwoLetterISORegionName\":\"EE\",\"ThreeLetterISORegionName\":\"EST\"},{\"Name\":\"ET\",\"DisplayName\":\"Ethiopia\",\"TwoLetterISORegionName\":\"ET\",\"ThreeLetterISORegionName\":\"ETH\"},{\"Name\":\"FO\",\"DisplayName\":\"Faroe Islands\",\"TwoLetterISORegionName\":\"FO\",\"ThreeLetterISORegionName\":\"FRO\"},{\"Name\":\"FI\",\"DisplayName\":\"Finland\",\"TwoLetterISORegionName\":\"FI\",\"ThreeLetterISORegionName\":\"FIN\"},{\"Name\":\"FR\",\"DisplayName\":\"France\",\"TwoLetterISORegionName\":\"FR\",\"ThreeLetterISORegionName\":\"FRA\"},{\"Name\":\"GE\",\"DisplayName\":\"Georgia\",\"TwoLetterISORegionName\":\"GE\",\"ThreeLetterISORegionName\":\"GEO\"},{\"Name\":\"DE\",\"DisplayName\":\"Germany\",\"TwoLetterISORegionName\":\"DE\",\"ThreeLetterISORegionName\":\"DEU\"},{\"Name\":\"GR\",\"DisplayName\":\"Greece\",\"TwoLetterISORegionName\":\"GR\",\"ThreeLetterISORegionName\":\"GRC\"},{\"Name\":\"GL\",\"DisplayName\":\"Greenland\",\"TwoLetterISORegionName\":\"GL\",\"ThreeLetterISORegionName\":\"GRL\"},{\"Name\":\"GT\",\"DisplayName\":\"Guatemala\",\"TwoLetterISORegionName\":\"GT\",\"ThreeLetterISORegionName\":\"GTM\"},{\"Name\":\"HT\",\"DisplayName\":\"Haiti\",\"TwoLetterISORegionName\":\"HT\",\"ThreeLetterISORegionName\":\"HTI\"},{\"Name\":\"HN\",\"DisplayName\":\"Honduras\",\"TwoLetterISORegionName\":\"HN\",\"ThreeLetterISORegionName\":\"HND\"},{\"Name\":\"HK\",\"DisplayName\":\"Hong Kong S.A.R.\",\"TwoLetterISORegionName\":\"HK\",\"ThreeLetterISORegionName\":\"HKG\"},{\"Name\":\"HU\",\"DisplayName\":\"Hungary\",\"TwoLetterISORegionName\":\"HU\",\"ThreeLetterISORegionName\":\"HUN\"},{\"Name\":\"IS\",\"DisplayName\":\"Iceland\",\"TwoLetterISORegionName\":\"IS\",\"ThreeLetterISORegionName\":\"ISL\"},{\"Name\":\"IN\",\"DisplayName\":\"India\",\"TwoLetterISORegionName\":\"IN\",\"ThreeLetterISORegionName\":\"IND\"},{\"Name\":\"ID\",\"DisplayName\":\"Indonesia\",\"TwoLetterISORegionName\":\"ID\",\"ThreeLetterISORegionName\":\"IDN\"},{\"Name\":\"IR\",\"DisplayName\":\"Iran\",\"TwoLetterISORegionName\":\"IR\",\"ThreeLetterISORegionName\":\"IRN\"},{\"Name\":\"IQ\",\"DisplayName\":\"Iraq\",\"TwoLetterISORegionName\":\"IQ\",\"ThreeLetterISORegionName\":\"IRQ\"},{\"Name\":\"IE\",\"DisplayName\":\"Ireland\",\"TwoLetterISORegionName\":\"IE\",\"ThreeLetterISORegionName\":\"IRL\"},{\"Name\":\"PK\",\"DisplayName\":\"Islamic Republic of Pakistan\",\"TwoLetterISORegionName\":\"PK\",\"ThreeLetterISORegionName\":\"PAK\"},{\"Name\":\"IL\",\"DisplayName\":\"Israel\",\"TwoLetterISORegionName\":\"IL\",\"ThreeLetterISORegionName\":\"ISR\"},{\"Name\":\"IT\",\"DisplayName\":\"Italy\",\"TwoLetterISORegionName\":\"IT\",\"ThreeLetterISORegionName\":\"ITA\"},{\"Name\":\"CI\",\"DisplayName\":\"Ivory Coast\",\"TwoLetterISORegionName\":\"CI\",\"ThreeLetterISORegionName\":\"CIV\"},{\"Name\":\"JM\",\"DisplayName\":\"Jamaica\",\"TwoLetterISORegionName\":\"JM\",\"ThreeLetterISORegionName\":\"JAM\"},{\"Name\":\"JP\",\"DisplayName\":\"Japan\",\"TwoLetterISORegionName\":\"JP\",\"ThreeLetterISORegionName\":\"JPN\"},{\"Name\":\"JO\",\"DisplayName\":\"Jordan\",\"TwoLetterISORegionName\":\"JO\",\"ThreeLetterISORegionName\":\"JOR\"},{\"Name\":\"KZ\",\"DisplayName\":\"Kazakhstan\",\"TwoLetterISORegionName\":\"KZ\",\"ThreeLetterISORegionName\":\"KAZ\"},{\"Name\":\"KE\",\"DisplayName\":\"Kenya\",\"TwoLetterISORegionName\":\"KE\",\"ThreeLetterISORegionName\":\"KEN\"},{\"Name\":\"KR\",\"DisplayName\":\"Korea\",\"TwoLetterISORegionName\":\"KR\",\"ThreeLetterISORegionName\":\"KOR\"},{\"Name\":\"KW\",\"DisplayName\":\"Kuwait\",\"TwoLetterISORegionName\":\"KW\",\"ThreeLetterISORegionName\":\"KWT\"},{\"Name\":\"KG\",\"DisplayName\":\"Kyrgyzstan\",\"TwoLetterISORegionName\":\"KG\",\"ThreeLetterISORegionName\":\"KGZ\"},{\"Name\":\"LA\",\"DisplayName\":\"Lao P.D.R.\",\"TwoLetterISORegionName\":\"LA\",\"ThreeLetterISORegionName\":\"LAO\"},{\"Name\":\"419\",\"DisplayName\":\"Latin America\",\"TwoLetterISORegionName\":\"419\",\"ThreeLetterISORegionName\":\"419\"},{\"Name\":\"LV\",\"DisplayName\":\"Latvia\",\"TwoLetterISORegionName\":\"LV\",\"ThreeLetterISORegionName\":\"LVA\"},{\"Name\":\"LB\",\"DisplayName\":\"Lebanon\",\"TwoLetterISORegionName\":\"LB\",\"ThreeLetterISORegionName\":\"LBN\"},{\"Name\":\"LY\",\"DisplayName\":\"Libya\",\"TwoLetterISORegionName\":\"LY\",\"ThreeLetterISORegionName\":\"LBY\"},{\"Name\":\"LI\",\"DisplayName\":\"Liechtenstein\",\"TwoLetterISORegionName\":\"LI\",\"ThreeLetterISORegionName\":\"LIE\"},{\"Name\":\"LT\",\"DisplayName\":\"Lithuania\",\"TwoLetterISORegionName\":\"LT\",\"ThreeLetterISORegionName\":\"LTU\"},{\"Name\":\"LU\",\"DisplayName\":\"Luxembourg\",\"TwoLetterISORegionName\":\"LU\",\"ThreeLetterISORegionName\":\"LUX\"},{\"Name\":\"MO\",\"DisplayName\":\"Macao S.A.R.\",\"TwoLetterISORegionName\":\"MO\",\"ThreeLetterISORegionName\":\"MAC\"},{\"Name\":\"MK\",\"DisplayName\":\"Macedonia (FYROM)\",\"TwoLetterISORegionName\":\"MK\",\"ThreeLetterISORegionName\":\"MKD\"},{\"Name\":\"MY\",\"DisplayName\":\"Malaysia\",\"TwoLetterISORegionName\":\"MY\",\"ThreeLetterISORegionName\":\"MYS\"},{\"Name\":\"MV\",\"DisplayName\":\"Maldives\",\"TwoLetterISORegionName\":\"MV\",\"ThreeLetterISORegionName\":\"MDV\"},{\"Name\":\"ML\",\"DisplayName\":\"Mali\",\"TwoLetterISORegionName\":\"ML\",\"ThreeLetterISORegionName\":\"MLI\"},{\"Name\":\"MT\",\"DisplayName\":\"Malta\",\"TwoLetterISORegionName\":\"MT\",\"ThreeLetterISORegionName\":\"MLT\"},{\"Name\":\"MX\",\"DisplayName\":\"Mexico\",\"TwoLetterISORegionName\":\"MX\",\"ThreeLetterISORegionName\":\"MEX\"},{\"Name\":\"MN\",\"DisplayName\":\"Mongolia\",\"TwoLetterISORegionName\":\"MN\",\"ThreeLetterISORegionName\":\"MNG\"},{\"Name\":\"ME\",\"DisplayName\":\"Montenegro\",\"TwoLetterISORegionName\":\"ME\",\"ThreeLetterISORegionName\":\"MNE\"},{\"Name\":\"MA\",\"DisplayName\":\"Morocco\",\"TwoLetterISORegionName\":\"MA\",\"ThreeLetterISORegionName\":\"MAR\"},{\"Name\":\"NP\",\"DisplayName\":\"Nepal\",\"TwoLetterISORegionName\":\"NP\",\"ThreeLetterISORegionName\":\"NPL\"},{\"Name\":\"NL\",\"DisplayName\":\"Netherlands\",\"TwoLetterISORegionName\":\"NL\",\"ThreeLetterISORegionName\":\"NLD\"},{\"Name\":\"NZ\",\"DisplayName\":\"New Zealand\",\"TwoLetterISORegionName\":\"NZ\",\"ThreeLetterISORegionName\":\"NZL\"},{\"Name\":\"NI\",\"DisplayName\":\"Nicaragua\",\"TwoLetterISORegionName\":\"NI\",\"ThreeLetterISORegionName\":\"NIC\"},{\"Name\":\"NG\",\"DisplayName\":\"Nigeria\",\"TwoLetterISORegionName\":\"NG\",\"ThreeLetterISORegionName\":\"NGA\"},{\"Name\":\"NO\",\"DisplayName\":\"Norway\",\"TwoLetterISORegionName\":\"NO\",\"ThreeLetterISORegionName\":\"NOR\"},{\"Name\":\"OM\",\"DisplayName\":\"Oman\",\"TwoLetterISORegionName\":\"OM\",\"ThreeLetterISORegionName\":\"OMN\"},{\"Name\":\"PA\",\"DisplayName\":\"Panama\",\"TwoLetterISORegionName\":\"PA\",\"ThreeLetterISORegionName\":\"PAN\"},{\"Name\":\"PY\",\"DisplayName\":\"Paraguay\",\"TwoLetterISORegionName\":\"PY\",\"ThreeLetterISORegionName\":\"PRY\"},{\"Name\":\"CN\",\"DisplayName\":\"People's Republic of China\",\"TwoLetterISORegionName\":\"CN\",\"ThreeLetterISORegionName\":\"CHN\"},{\"Name\":\"PE\",\"DisplayName\":\"Peru\",\"TwoLetterISORegionName\":\"PE\",\"ThreeLetterISORegionName\":\"PER\"},{\"Name\":\"PH\",\"DisplayName\":\"Philippines\",\"TwoLetterISORegionName\":\"PH\",\"ThreeLetterISORegionName\":\"PHL\"},{\"Name\":\"PL\",\"DisplayName\":\"Poland\",\"TwoLetterISORegionName\":\"PL\",\"ThreeLetterISORegionName\":\"POL\"},{\"Name\":\"PT\",\"DisplayName\":\"Portugal\",\"TwoLetterISORegionName\":\"PT\",\"ThreeLetterISORegionName\":\"PRT\"},{\"Name\":\"MC\",\"DisplayName\":\"Principality of Monaco\",\"TwoLetterISORegionName\":\"MC\",\"ThreeLetterISORegionName\":\"MCO\"},{\"Name\":\"PR\",\"DisplayName\":\"Puerto Rico\",\"TwoLetterISORegionName\":\"PR\",\"ThreeLetterISORegionName\":\"PRI\"},{\"Name\":\"QA\",\"DisplayName\":\"Qatar\",\"TwoLetterISORegionName\":\"QA\",\"ThreeLetterISORegionName\":\"QAT\"},{\"Name\":\"MD\",\"DisplayName\":\"Republica Moldova\",\"TwoLetterISORegionName\":\"MD\",\"ThreeLetterISORegionName\":\"MDA\"},{\"Name\":\"RE\",\"DisplayName\":\"Réunion\",\"TwoLetterISORegionName\":\"RE\",\"ThreeLetterISORegionName\":\"REU\"},{\"Name\":\"RO\",\"DisplayName\":\"Romania\",\"TwoLetterISORegionName\":\"RO\",\"ThreeLetterISORegionName\":\"ROU\"},{\"Name\":\"RU\",\"DisplayName\":\"Russia\",\"TwoLetterISORegionName\":\"RU\",\"ThreeLetterISORegionName\":\"RUS\"},{\"Name\":\"RW\",\"DisplayName\":\"Rwanda\",\"TwoLetterISORegionName\":\"RW\",\"ThreeLetterISORegionName\":\"RWA\"},{\"Name\":\"SA\",\"DisplayName\":\"Saudi Arabia\",\"TwoLetterISORegionName\":\"SA\",\"ThreeLetterISORegionName\":\"SAU\"},{\"Name\":\"SN\",\"DisplayName\":\"Senegal\",\"TwoLetterISORegionName\":\"SN\",\"ThreeLetterISORegionName\":\"SEN\"},{\"Name\":\"RS\",\"DisplayName\":\"Serbia\",\"TwoLetterISORegionName\":\"RS\",\"ThreeLetterISORegionName\":\"SRB\"},{\"Name\":\"CS\",\"DisplayName\":\"Serbia and Montenegro (Former)\",\"TwoLetterISORegionName\":\"CS\",\"ThreeLetterISORegionName\":\"SCG\"},{\"Name\":\"SG\",\"DisplayName\":\"Singapore\",\"TwoLetterISORegionName\":\"SG\",\"ThreeLetterISORegionName\":\"SGP\"},{\"Name\":\"SK\",\"DisplayName\":\"Slovakia\",\"TwoLetterISORegionName\":\"SK\",\"ThreeLetterISORegionName\":\"SVK\"},{\"Name\":\"SI\",\"DisplayName\":\"Slovenia\",\"TwoLetterISORegionName\":\"SI\",\"ThreeLetterISORegionName\":\"SVN\"},{\"Name\":\"SO\",\"DisplayName\":\"Soomaaliya\",\"TwoLetterISORegionName\":\"SO\",\"ThreeLetterISORegionName\":\"SOM\"},{\"Name\":\"ZA\",\"DisplayName\":\"South Africa\",\"TwoLetterISORegionName\":\"ZA\",\"ThreeLetterISORegionName\":\"ZAF\"},{\"Name\":\"ES\",\"DisplayName\":\"Spain\",\"TwoLetterISORegionName\":\"ES\",\"ThreeLetterISORegionName\":\"ESP\"},{\"Name\":\"LK\",\"DisplayName\":\"Sri Lanka\",\"TwoLetterISORegionName\":\"LK\",\"ThreeLetterISORegionName\":\"LKA\"},{\"Name\":\"SE\",\"DisplayName\":\"Sweden\",\"TwoLetterISORegionName\":\"SE\",\"ThreeLetterISORegionName\":\"SWE\"},{\"Name\":\"CH\",\"DisplayName\":\"Switzerland\",\"TwoLetterISORegionName\":\"CH\",\"ThreeLetterISORegionName\":\"CHE\"},{\"Name\":\"SY\",\"DisplayName\":\"Syria\",\"TwoLetterISORegionName\":\"SY\",\"ThreeLetterISORegionName\":\"SYR\"},{\"Name\":\"TW\",\"DisplayName\":\"Taiwan\",\"TwoLetterISORegionName\":\"TW\",\"ThreeLetterISORegionName\":\"TWN\"},{\"Name\":\"TJ\",\"DisplayName\":\"Tajikistan\",\"TwoLetterISORegionName\":\"TJ\",\"ThreeLetterISORegionName\":\"TAJ\"},{\"Name\":\"TH\",\"DisplayName\":\"Thailand\",\"TwoLetterISORegionName\":\"TH\",\"ThreeLetterISORegionName\":\"THA\"},{\"Name\":\"TT\",\"DisplayName\":\"Trinidad and Tobago\",\"TwoLetterISORegionName\":\"TT\",\"ThreeLetterISORegionName\":\"TTO\"},{\"Name\":\"TN\",\"DisplayName\":\"Tunisia\",\"TwoLetterISORegionName\":\"TN\",\"ThreeLetterISORegionName\":\"TUN\"},{\"Name\":\"TR\",\"DisplayName\":\"Turkey\",\"TwoLetterISORegionName\":\"TR\",\"ThreeLetterISORegionName\":\"TUR\"},{\"Name\":\"TM\",\"DisplayName\":\"Turkmenistan\",\"TwoLetterISORegionName\":\"TM\",\"ThreeLetterISORegionName\":\"TKM\"},{\"Name\":\"AE\",\"DisplayName\":\"U.A.E.\",\"TwoLetterISORegionName\":\"AE\",\"ThreeLetterISORegionName\":\"ARE\"},{\"Name\":\"UA\",\"DisplayName\":\"Ukraine\",\"TwoLetterISORegionName\":\"UA\",\"ThreeLetterISORegionName\":\"UKR\"},{\"Name\":\"GB\",\"DisplayName\":\"United Kingdom\",\"TwoLetterISORegionName\":\"GB\",\"ThreeLetterISORegionName\":\"GBR\"},{\"Name\":\"US\",\"DisplayName\":\"United States\",\"TwoLetterISORegionName\":\"US\",\"ThreeLetterISORegionName\":\"USA\"},{\"Name\":\"UY\",\"DisplayName\":\"Uruguay\",\"TwoLetterISORegionName\":\"UY\",\"ThreeLetterISORegionName\":\"URY\"},{\"Name\":\"UZ\",\"DisplayName\":\"Uzbekistan\",\"TwoLetterISORegionName\":\"UZ\",\"ThreeLetterISORegionName\":\"UZB\"},{\"Name\":\"VN\",\"DisplayName\":\"Vietnam\",\"TwoLetterISORegionName\":\"VN\",\"ThreeLetterISORegionName\":\"VNM\"},{\"Name\":\"YE\",\"DisplayName\":\"Yemen\",\"TwoLetterISORegionName\":\"YE\",\"ThreeLetterISORegionName\":\"YEM\"},{\"Name\":\"ZW\",\"DisplayName\":\"Zimbabwe\",\"TwoLetterISORegionName\":\"ZW\",\"ThreeLetterISORegionName\":\"ZWE\"}]";
- using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
- {
- return _jsonSerializer.DeserializeFromStream<CountryInfo[]>(stream);
- }
+ return _jsonSerializer.DeserializeFromString<CountryInfo[]>(jsonCountries);
}
/// <summary>
@@ -278,7 +345,7 @@ namespace Emby.Server.Implementations.Localization
.Split('-')
.Last();
- _allParentalRatings.TryAdd(countryCode, dict);
+ _allParentalRatings[countryCode] = dict;
}
private readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
@@ -305,19 +372,34 @@ namespace Emby.Server.Implementations.Localization
ParentalRating value;
- if (!ratingsDictionary.TryGetValue(rating, out value))
+ if (ratingsDictionary.TryGetValue(rating, out value))
{
- // If we don't find anything check all ratings systems
- foreach (var dictionary in _allParentalRatings.Values)
+ return value.Value;
+ }
+
+ // If we don't find anything check all ratings systems
+ foreach (var dictionary in _allParentalRatings.Values)
+ {
+ if (dictionary.TryGetValue(rating, out value))
{
- if (dictionary.TryGetValue(rating, out value))
- {
- return value.Value;
- }
+ return value.Value;
+ }
+ }
+
+ // Try splitting by : to handle "Germany: FSK 18"
+ var index = rating.IndexOf(':');
+ if (index != -1)
+ {
+ rating = rating.Substring(index).TrimStart(':').Trim();
+
+ if (!string.IsNullOrWhiteSpace(rating))
+ {
+ return GetRatingLevel(rating);
}
}
- return value == null ? (int?)null : value.Value;
+ // TODO: Further improve by normalizing out all spaces and dashes
+ return null;
}
public bool HasUnicodeCategory(string value, UnicodeCategory category)
@@ -340,11 +422,11 @@ namespace Emby.Server.Implementations.Localization
public string GetLocalizedString(string phrase, string culture)
{
- if (string.IsNullOrWhiteSpace(culture))
+ if (string.IsNullOrEmpty(culture))
{
culture = _configurationManager.Configuration.UICulture;
}
- if (string.IsNullOrWhiteSpace(culture))
+ if (string.IsNullOrEmpty(culture))
{
culture = DefaultCulture;
}
@@ -368,7 +450,7 @@ namespace Emby.Server.Implementations.Localization
public Dictionary<string, string> GetLocalizationDictionary(string culture)
{
- if (string.IsNullOrWhiteSpace(culture))
+ if (string.IsNullOrEmpty(culture))
{
throw new ArgumentNullException("culture");
}
@@ -381,7 +463,7 @@ namespace Emby.Server.Implementations.Localization
private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
{
- if (string.IsNullOrWhiteSpace(culture))
+ if (string.IsNullOrEmpty(culture))
{
throw new ArgumentNullException("culture");
}
diff --git a/Emby.Server.Implementations/Localization/Ratings/au.txt b/Emby.Server.Implementations/Localization/Ratings/au.txt
deleted file mode 100644
index fa60f5305..000000000
--- a/Emby.Server.Implementations/Localization/Ratings/au.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-AU-G,1
-AU-PG,5
-AU-M,6
-AU-MA15+,7
-AU-M15+,8
-AU-R18+,9
-AU-X18+,10
-AU-RC,11
diff --git a/Emby.Server.Implementations/Localization/Ratings/be.txt b/Emby.Server.Implementations/Localization/Ratings/be.txt
deleted file mode 100644
index 99a53f664..000000000
--- a/Emby.Server.Implementations/Localization/Ratings/be.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-BE-AL,1
-BE-MG6,2
-BE-6,3
-BE-9,5
-BE-12,6
-BE-16,8 \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Ratings/de.txt b/Emby.Server.Implementations/Localization/Ratings/de.txt
deleted file mode 100644
index ad1f18619..000000000
--- a/Emby.Server.Implementations/Localization/Ratings/de.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-DE-0,1
-FSK-0,1
-DE-6,5
-FSK-6,5
-DE-12,7
-FSK-12,7
-DE-16,8
-FSK-16,8
-DE-18,9
-FSK-18,9 \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Ratings/ru.txt b/Emby.Server.Implementations/Localization/Ratings/ru.txt
deleted file mode 100644
index 1bc94affd..000000000
--- a/Emby.Server.Implementations/Localization/Ratings/ru.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-RU-0+,1
-RU-6+,3
-RU-12+,7
-RU-16+,9
-RU-18+,10
diff --git a/Emby.Server.Implementations/Localization/Ratings/us.txt b/Emby.Server.Implementations/Localization/Ratings/us.txt
index 9bd78c72b..eebd828c7 100644
--- a/Emby.Server.Implementations/Localization/Ratings/us.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/us.txt
@@ -1,9 +1,9 @@
+TV-Y,1
APPROVED,1
G,1
E,1
EC,1
TV-G,1
-TV-Y,2
TV-Y7,3
TV-Y7-FV,4
PG,5
diff --git a/Emby.Server.Implementations/Localization/iso6392.txt b/Emby.Server.Implementations/Localization/iso6392.txt
index 5616d41bc..a7e7cfc20 100644
--- a/Emby.Server.Implementations/Localization/iso6392.txt
+++ b/Emby.Server.Implementations/Localization/iso6392.txt
@@ -403,7 +403,7 @@ sot||st|Sotho, Southern|sotho du Sud
spa||es|Spanish; Castilian|espagnol; castillan
srd||sc|Sardinian|sarde
srn|||Sranan Tongo|sranan tongo
-srp||sr|Serbian|serbe
+srp|scc|sr|Serbian|serbe
srr|||Serer|sérère
ssa|||Nilo-Saharan languages|nilo-sahariennes, langues
ssw||ss|Swati|swati
diff --git a/Emby.Server.Implementations/Logging/SimpleLogManager.cs b/Emby.Server.Implementations/Logging/SimpleLogManager.cs
index 6129f38c4..390814c34 100644
--- a/Emby.Server.Implementations/Logging/SimpleLogManager.cs
+++ b/Emby.Server.Implementations/Logging/SimpleLogManager.cs
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Logging
return new NamedLogger(name, this);
}
- public void ReloadLogger(LogSeverity severity)
+ public async Task ReloadLogger(LogSeverity severity, CancellationToken cancellationToken)
{
LogSeverity = severity;
@@ -39,19 +39,23 @@ namespace Emby.Server.Implementations.Logging
if (logger != null)
{
logger.Dispose();
+ await TryMoveToArchive(logger.Path, cancellationToken).ConfigureAwait(false);
}
- var path = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Floor(DateTime.Now.Ticks / 10000000) + ".txt");
+ var newPath = Path.Combine(LogDirectory, LogFilePrefix + ".txt");
- _fileLogger = new FileLogger(path);
+ if (File.Exists(newPath))
+ {
+ newPath = await TryMoveToArchive(newPath, cancellationToken).ConfigureAwait(false);
+ }
+
+ _fileLogger = new FileLogger(newPath);
if (LoggerLoaded != null)
{
try
{
-
LoggerLoaded(this, EventArgs.Empty);
-
}
catch (Exception ex)
{
@@ -60,6 +64,42 @@ namespace Emby.Server.Implementations.Logging
}
}
+ private async Task<string> TryMoveToArchive(string file, CancellationToken cancellationToken, int retryCount = 0)
+ {
+ var archivePath = GetArchiveFilePath();
+
+ try
+ {
+ File.Move(file, archivePath);
+
+ return file;
+ }
+ catch (FileNotFoundException)
+ {
+ return file;
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return file;
+ }
+ catch
+ {
+ if (retryCount >= 50)
+ {
+ return GetArchiveFilePath();
+ }
+
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+
+ return await TryMoveToArchive(file, cancellationToken, retryCount + 1).ConfigureAwait(false);
+ }
+ }
+
+ private string GetArchiveFilePath()
+ {
+ return Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Floor(DateTime.Now.Ticks / 10000000) + ".txt");
+ }
+
public event EventHandler LoggerLoaded;
public void Flush()
@@ -104,10 +144,12 @@ namespace Emby.Server.Implementations.Logging
if (logger != null)
{
logger.Dispose();
+
+ var task = TryMoveToArchive(logger.Path, CancellationToken.None);
+ Task.WaitAll(task);
}
_fileLogger = null;
- GC.SuppressFinalize(this);
}
}
@@ -119,9 +161,13 @@ namespace Emby.Server.Implementations.Logging
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly BlockingCollection<string> _queue = new BlockingCollection<string>();
+ public string Path { get; set; }
+
public FileLogger(string path)
{
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ Path = path;
+
+ Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
_fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 32768);
_cancellationTokenSource = new CancellationTokenSource();
@@ -144,6 +190,10 @@ namespace Emby.Server.Implementations.Logging
}
_fileStream.Write(bytes, 0, bytes.Length);
+ if (_disposed)
+ {
+ return;
+ }
_fileStream.Flush(true);
}
@@ -177,13 +227,22 @@ namespace Emby.Server.Implementations.Logging
public void Dispose()
{
- _cancellationTokenSource.Cancel();
-
- Flush();
+ if (_disposed)
+ {
+ return;
+ }
_disposed = true;
- _fileStream.Dispose();
- GC.SuppressFinalize(this);
+ _cancellationTokenSource.Cancel();
+
+ var stream = _fileStream;
+ if (stream != null)
+ {
+ using (stream)
+ {
+ stream.Flush(true);
+ }
+ }
}
}
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index d790f4ab8..c6033b4f4 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.MediaEncoder
/// Gets the chapter images data path.
/// </summary>
/// <value>The chapter images data path.</value>
- private string GetChapterImagesPath(IHasMetadata item)
+ private string GetChapterImagesPath(BaseItem item)
{
return Path.Combine(item.GetInternalMetadataPath(), "chapters");
}
@@ -133,6 +133,8 @@ namespace Emby.Server.Implementations.MediaEncoder
{
if (extractImages)
{
+ cancellationToken.ThrowIfCancellationRequested();
+
try
{
// Add some time for the first chapter to make sure we don't end up with a black image
@@ -140,7 +142,7 @@ namespace Emby.Server.Implementations.MediaEncoder
var protocol = MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, new string[] { });
+ var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, Array.Empty<string>());
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
diff --git a/Emby.Server.Implementations/Migrations/IVersionMigration.cs b/Emby.Server.Implementations/Migrations/IVersionMigration.cs
deleted file mode 100644
index 7804912e3..000000000
--- a/Emby.Server.Implementations/Migrations/IVersionMigration.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Threading.Tasks;
-
-namespace Emby.Server.Implementations.Migrations
-{
- public interface IVersionMigration
- {
- Task Run();
- }
-}
diff --git a/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs b/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs
index b18335da7..b721e8a26 100644
--- a/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs
+++ b/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs
@@ -54,16 +54,9 @@ namespace Emby.Server.Implementations.Net
/// <seealso cref="IsDisposed"/>
public void Dispose()
{
- try
- {
- IsDisposed = true;
+ IsDisposed = true;
- Dispose(true);
- }
- finally
- {
- GC.SuppressFinalize(this);
- }
+ Dispose(true);
}
#endregion
diff --git a/MediaBrowser.Controller/Net/IWebSocket.cs b/Emby.Server.Implementations/Net/IWebSocket.cs
index b88f2c389..f79199a07 100644
--- a/MediaBrowser.Controller/Net/IWebSocket.cs
+++ b/Emby.Server.Implementations/Net/IWebSocket.cs
@@ -1,9 +1,9 @@
-using MediaBrowser.Model.Net;
-using System;
+using System;
using System.Threading;
using System.Threading.Tasks;
+using System.Net.WebSockets;
-namespace MediaBrowser.Controller.Net
+namespace Emby.Server.Implementations.Net
{
/// <summary>
/// Interface IWebSocket
@@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Net
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>
@@ -51,4 +45,9 @@ namespace MediaBrowser.Controller.Net
/// <returns>Task.</returns>
Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken);
}
+
+ public interface IMemoryWebSocket
+ {
+ Action<Memory<byte>, int> OnReceiveMemoryBytes { get; set; }
+ }
}
diff --git a/Emby.Server.Implementations/Net/NetAcceptSocket.cs b/Emby.Server.Implementations/Net/NetAcceptSocket.cs
deleted file mode 100644
index d80341a07..000000000
--- a/Emby.Server.Implementations/Net/NetAcceptSocket.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.Networking;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-
-namespace Emby.Server.Implementations.Net
-{
- public class NetAcceptSocket : IAcceptSocket
- {
- public Socket Socket { get; private set; }
- private readonly ILogger _logger;
-
- public bool DualMode { get; private set; }
-
- public NetAcceptSocket(Socket socket, ILogger logger, bool isDualMode)
- {
- if (socket == null)
- {
- throw new ArgumentNullException("socket");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- Socket = socket;
- _logger = logger;
- DualMode = isDualMode;
- }
-
- public IpEndPointInfo LocalEndPoint
- {
- get
- {
- return NetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.LocalEndPoint);
- }
- }
-
- public IpEndPointInfo RemoteEndPoint
- {
- get
- {
- return NetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.RemoteEndPoint);
- }
- }
-
- public void Connect(IpEndPointInfo endPoint)
- {
- var nativeEndpoint = NetworkManager.ToIPEndPoint(endPoint);
-
- Socket.Connect(nativeEndpoint);
- }
-
- public void Close()
- {
-#if NET46
- Socket.Close();
-#else
- Socket.Dispose();
-#endif
- }
-
- public void Shutdown(bool both)
- {
- if (both)
- {
- Socket.Shutdown(SocketShutdown.Both);
- }
- else
- {
- // Change interface if ever needed
- throw new NotImplementedException();
- }
- }
-
- public void Listen(int backlog)
- {
- Socket.Listen(backlog);
- }
-
- public void Bind(IpEndPointInfo endpoint)
- {
- var nativeEndpoint = NetworkManager.ToIPEndPoint(endpoint);
-
- Socket.Bind(nativeEndpoint);
- }
-
- public void Dispose()
- {
- Socket.Dispose();
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index bdae1728f..9726ef097 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -29,41 +29,6 @@ namespace Emby.Server.Implementations.Net
_logger = logger;
}
- public IAcceptSocket CreateSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode)
- {
- try
- {
- var addressFamily = family == IpAddressFamily.InterNetwork
- ? AddressFamily.InterNetwork
- : AddressFamily.InterNetworkV6;
-
- var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
-
- if (dualMode)
- {
- socket.DualMode = true;
- }
-
- return new NetAcceptSocket(socket, _logger, dualMode);
- }
- catch (SocketException ex)
- {
- throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
- }
- catch (ArgumentException ex)
- {
- if (dualMode)
- {
- // Mono for BSD incorrectly throws ArgumentException instead of SocketException
- throw new SocketCreateException("AddressFamilyNotSupported", ex);
- }
- else
- {
- throw;
- }
- }
- }
-
public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort)
{
if (remotePort < 0) throw new ArgumentException("remotePort cannot be less than zero.", "remotePort");
diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs
index 58e4d6f89..523ca3752 100644
--- a/Emby.Server.Implementations/Net/UdpSocket.cs
+++ b/Emby.Server.Implementations/Net/UdpSocket.cs
@@ -118,6 +118,8 @@ namespace Emby.Server.Implementations.Net
public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback)
{
+ ThrowIfDisposed();
+
EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
return _Socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer);
@@ -125,17 +127,21 @@ namespace Emby.Server.Implementations.Net
public int Receive(byte[] buffer, int offset, int count)
{
+ ThrowIfDisposed();
+
return _Socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
}
public SocketReceiveResult EndReceive(IAsyncResult result)
{
+ ThrowIfDisposed();
+
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint remoteEndPoint = (EndPoint)sender;
var receivedBytes = _Socket.EndReceiveFrom(result, ref remoteEndPoint);
- var buffer = (byte[]) result.AsyncState;
+ var buffer = (byte[])result.AsyncState;
return new SocketReceiveResult
{
@@ -148,13 +154,20 @@ namespace Emby.Server.Implementations.Net
public Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
+ ThrowIfDisposed();
+
var taskCompletion = new TaskCompletionSource<SocketReceiveResult>();
+ bool isResultSet = false;
Action<IAsyncResult> callback = callbackResult =>
{
try
{
- taskCompletion.TrySetResult(EndReceive(callbackResult));
+ if (!isResultSet)
+ {
+ isResultSet = true;
+ taskCompletion.TrySetResult(EndReceive(callbackResult));
+ }
}
catch (Exception ex)
{
@@ -167,6 +180,7 @@ namespace Emby.Server.Implementations.Net
if (result.CompletedSynchronously)
{
callback(result);
+ return taskCompletion.Task;
}
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
@@ -176,6 +190,8 @@ namespace Emby.Server.Implementations.Net
public Task<SocketReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
{
+ ThrowIfDisposed();
+
var buffer = new byte[8192];
return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken);
@@ -183,13 +199,20 @@ namespace Emby.Server.Implementations.Net
public Task SendToAsync(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken)
{
+ ThrowIfDisposed();
+
var taskCompletion = new TaskCompletionSource<int>();
+ bool isResultSet = false;
Action<IAsyncResult> callback = callbackResult =>
{
try
{
- taskCompletion.TrySetResult(EndSendTo(callbackResult));
+ if (!isResultSet)
+ {
+ isResultSet = true;
+ taskCompletion.TrySetResult(EndSendTo(callbackResult));
+ }
}
catch (Exception ex)
{
@@ -202,6 +225,7 @@ namespace Emby.Server.Implementations.Net
if (result.CompletedSynchronously)
{
callback(result);
+ return taskCompletion.Task;
}
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
@@ -211,6 +235,8 @@ namespace Emby.Server.Implementations.Net
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, AsyncCallback callback, object state)
{
+ ThrowIfDisposed();
+
var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint);
return _Socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state);
@@ -218,6 +244,8 @@ namespace Emby.Server.Implementations.Net
public int EndSendTo(IAsyncResult result)
{
+ ThrowIfDisposed();
+
return _Socket.EndSendTo(result);
}
diff --git a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs
new file mode 100644
index 000000000..7b7f12d50
--- /dev/null
+++ b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MediaBrowser.Model.Services;
+
+namespace Emby.Server.Implementations.Net
+{
+ public class WebSocketConnectEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the URL.
+ /// </summary>
+ /// <value>The URL.</value>
+ public string Url { get; set; }
+ /// <summary>
+ /// Gets or sets the query string.
+ /// </summary>
+ /// <value>The query string.</value>
+ public QueryParamCollection QueryString { get; set; }
+ /// <summary>
+ /// Gets or sets the web socket.
+ /// </summary>
+ /// <value>The web socket.</value>
+ public IWebSocket WebSocket { get; set; }
+ /// <summary>
+ /// Gets or sets the endpoint.
+ /// </summary>
+ /// <value>The endpoint.</value>
+ public string Endpoint { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs b/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs
new file mode 100644
index 000000000..afb202fa3
--- /dev/null
+++ b/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs
@@ -0,0 +1,168 @@
+using System.Collections.Generic;
+
+namespace System.Net
+{
+ using System;
+ using System.Numerics;
+ using System.Text;
+
+ /// <summary>
+ /// Extension methods to convert <see cref="System.Numerics.BigInteger"/>
+ /// instances to hexadecimal, octal, and binary strings.
+ /// </summary>
+ public static class BigIntegerExtensions
+ {
+ /// <summary>
+ /// Converts a <see cref="BigInteger"/> to a binary string.
+ /// </summary>
+ /// <param name="bigint">A <see cref="BigInteger"/>.</param>
+ /// <returns>
+ /// A <see cref="System.String"/> containing a binary
+ /// representation of the supplied <see cref="BigInteger"/>.
+ /// </returns>
+ public static string ToBinaryString(this BigInteger bigint)
+ {
+ var bytes = bigint.ToByteArray();
+ var idx = bytes.Length - 1;
+
+ // Create a StringBuilder having appropriate capacity.
+ var base2 = new StringBuilder(bytes.Length * 8);
+
+ // Convert first byte to binary.
+ var binary = Convert.ToString(bytes[idx], 2);
+
+ // Ensure leading zero exists if value is positive.
+ if (binary[0] != '0' && bigint.Sign == 1)
+ {
+ base2.Append('0');
+ }
+
+ // Append binary string to StringBuilder.
+ base2.Append(binary);
+
+ // Convert remaining bytes adding leading zeros.
+ for (idx--; idx >= 0; idx--)
+ {
+ base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
+ }
+
+ return base2.ToString();
+ }
+
+ /// <summary>
+ /// Converts a <see cref="BigInteger"/> to a hexadecimal string.
+ /// </summary>
+ /// <param name="bigint">A <see cref="BigInteger"/>.</param>
+ /// <returns>
+ /// A <see cref="System.String"/> containing a hexadecimal
+ /// representation of the supplied <see cref="BigInteger"/>.
+ /// </returns>
+ public static string ToHexadecimalString(this BigInteger bigint)
+ {
+ return bigint.ToString("X");
+ }
+
+ /// <summary>
+ /// Converts a <see cref="BigInteger"/> to a octal string.
+ /// </summary>
+ /// <param name="bigint">A <see cref="BigInteger"/>.</param>
+ /// <returns>
+ /// A <see cref="System.String"/> containing an octal
+ /// representation of the supplied <see cref="BigInteger"/>.
+ /// </returns>
+ public static string ToOctalString(this BigInteger bigint)
+ {
+ var bytes = bigint.ToByteArray();
+ var idx = bytes.Length - 1;
+
+ // Create a StringBuilder having appropriate capacity.
+ var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8);
+
+ // Calculate how many bytes are extra when byte array is split
+ // into three-byte (24-bit) chunks.
+ var extra = bytes.Length % 3;
+
+ // If no bytes are extra, use three bytes for first chunk.
+ if (extra == 0)
+ {
+ extra = 3;
+ }
+
+ // Convert first chunk (24-bits) to integer value.
+ int int24 = 0;
+ for (; extra != 0; extra--)
+ {
+ int24 <<= 8;
+ int24 += bytes[idx--];
+ }
+
+ // Convert 24-bit integer to octal without adding leading zeros.
+ var octal = Convert.ToString(int24, 8);
+
+ // Ensure leading zero exists if value is positive.
+ if (octal[0] != '0')
+ {
+ if (bigint.Sign == 1)
+ {
+ base8.Append('0');
+ }
+ }
+
+ // Append first converted chunk to StringBuilder.
+ base8.Append(octal);
+
+ // Convert remaining 24-bit chunks, adding leading zeros.
+ for (; idx >= 0; idx -= 3)
+ {
+ int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2];
+ base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0'));
+ }
+
+ return base8.ToString();
+ }
+
+ /// <summary>
+ ///
+ /// Reverse a Positive BigInteger ONLY
+ /// Bitwise ~ operator
+ ///
+ /// Input : FF FF FF FF
+ /// Width : 4
+ /// Result : 00 00 00 00
+ ///
+ ///
+ /// Input : 00 00 00 00
+ /// Width : 4
+ /// Result : FF FF FF FF
+ ///
+ /// Input : FF FF FF FF
+ /// Width : 8
+ /// Result : FF FF FF FF 00 00 00 00
+ ///
+ ///
+ /// Input : 00 00 00 00
+ /// Width : 8
+ /// Result : FF FF FF FF FF FF FF FF
+ ///
+ /// </summary>
+ /// <param name="input"></param>
+ /// <param name="width"></param>
+ /// <returns></returns>
+ public static BigInteger PositiveReverse(this BigInteger input, int width)
+ {
+
+ var result = new List<byte>();
+ var bytes = input.ToByteArray();
+ var work = new byte[width];
+ Array.Copy(bytes, 0, work, 0, bytes.Length - 1); // Length -1 : positive BigInteger
+
+ for (int i = 0; i < work.Length; i++)
+ {
+ result.Add((byte)(~work[i]));
+ }
+ result.Add(0); // positive BigInteger
+ return new BigInteger(result.ToArray());
+
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs
new file mode 100644
index 000000000..2b31a0a32
--- /dev/null
+++ b/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs
@@ -0,0 +1,104 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace System.Net
+{
+ public class IPAddressCollection : IEnumerable<IPAddress>, IEnumerator<IPAddress>
+ {
+
+ private IPNetwork _ipnetwork;
+ private BigInteger _enumerator;
+
+ internal IPAddressCollection(IPNetwork ipnetwork)
+ {
+ this._ipnetwork = ipnetwork;
+ this._enumerator = -1;
+ }
+
+
+ #region Count, Array, Enumerator
+
+ public BigInteger Count
+ {
+ get
+ {
+ return this._ipnetwork.Total;
+ }
+ }
+
+ public IPAddress this[BigInteger i]
+ {
+ get
+ {
+ if (i >= this.Count)
+ {
+ throw new ArgumentOutOfRangeException("i");
+ }
+ byte width = this._ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetwork ? (byte)32 : (byte)128;
+ IPNetworkCollection ipn = this._ipnetwork.Subnet(width);
+ return ipn[i].Network;
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator<IPAddress> IEnumerable<IPAddress>.GetEnumerator()
+ {
+ return this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this;
+ }
+
+ #region IEnumerator<IPNetwork> Members
+
+ public IPAddress Current
+ {
+ get { return this[this._enumerator]; }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ // nothing to dispose
+ return;
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+
+ object IEnumerator.Current
+ {
+ get { return this.Current; }
+ }
+
+ public bool MoveNext()
+ {
+ this._enumerator++;
+ if (this._enumerator >= this.Count)
+ {
+ return false;
+ }
+ return true;
+
+ }
+
+ public void Reset()
+ {
+ this._enumerator = -1;
+ }
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs
new file mode 100644
index 000000000..6d7785b90
--- /dev/null
+++ b/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs
@@ -0,0 +1,2170 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Sockets;
+using System.Numerics;
+using System.Text.RegularExpressions;
+
+namespace System.Net
+{
+ /// <summary>
+ /// IP Network utility class.
+ /// Use IPNetwork.Parse to create instances.
+ /// </summary>
+ public class IPNetwork : IComparable<IPNetwork>
+ {
+
+ #region properties
+
+ //private uint _network;
+ private BigInteger _ipaddress;
+ private AddressFamily _family;
+ //private uint _netmask;
+ //private uint _broadcast;
+ //private uint _firstUsable;
+ //private uint _lastUsable;
+ //private uint _usable;
+ private byte _cidr;
+
+ #endregion
+
+ #region accessors
+
+ private BigInteger _network
+ {
+ get
+ {
+ BigInteger uintNetwork = this._ipaddress & this._netmask;
+ return uintNetwork;
+ }
+ }
+
+ /// <summary>
+ /// Network address
+ /// </summary>
+ public IPAddress Network
+ {
+ get
+ {
+
+ return IPNetwork.ToIPAddress(this._network, this._family);
+ }
+ }
+
+ /// <summary>
+ /// Address Family
+ /// </summary>
+ public AddressFamily AddressFamily
+ {
+ get
+ {
+ return this._family;
+ }
+ }
+
+ private BigInteger _netmask
+ {
+ get
+ {
+ return IPNetwork.ToUint(this._cidr, this._family);
+ }
+ }
+
+ /// <summary>
+ /// Netmask
+ /// </summary>
+ public IPAddress Netmask
+ {
+ get
+ {
+ return IPNetwork.ToIPAddress(this._netmask, this._family);
+ }
+ }
+
+ private BigInteger _broadcast
+ {
+ get
+ {
+
+ int width = this._family == Sockets.AddressFamily.InterNetwork ? 4 : 16;
+ BigInteger uintBroadcast = this._network + this._netmask.PositiveReverse(width);
+ return uintBroadcast;
+ }
+ }
+
+ /// <summary>
+ /// Broadcast address
+ /// </summary>
+ public IPAddress Broadcast
+ {
+ get
+ {
+ if (this._family == Sockets.AddressFamily.InterNetworkV6)
+ {
+ return null;
+ }
+ return IPNetwork.ToIPAddress(this._broadcast, this._family);
+ }
+ }
+
+ /// <summary>
+ /// First usable IP adress in Network
+ /// </summary>
+ public IPAddress FirstUsable
+ {
+ get
+ {
+ BigInteger fisrt = this._family == Sockets.AddressFamily.InterNetworkV6
+ ? this._network
+ : (this.Usable <= 0) ? this._network : this._network + 1;
+ return IPNetwork.ToIPAddress(fisrt, this._family);
+ }
+ }
+
+ /// <summary>
+ /// Last usable IP adress in Network
+ /// </summary>
+ public IPAddress LastUsable
+ {
+ get
+ {
+ BigInteger last = this._family == Sockets.AddressFamily.InterNetworkV6
+ ? this._broadcast
+ : (this.Usable <= 0) ? this._network : this._broadcast - 1;
+ return IPNetwork.ToIPAddress(last, this._family);
+ }
+ }
+
+ /// <summary>
+ /// Number of usable IP adress in Network
+ /// </summary>
+ public BigInteger Usable
+ {
+ get
+ {
+
+ if (this._family == Sockets.AddressFamily.InterNetworkV6)
+ {
+ return this.Total;
+ }
+ byte[] mask = new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00 };
+ BigInteger bmask = new BigInteger(mask);
+ BigInteger usableIps = (_cidr > 30) ? 0 : ((bmask >> _cidr) - 1);
+ return usableIps;
+ }
+ }
+
+ /// <summary>
+ /// Number of IP adress in Network
+ /// </summary>
+ public BigInteger Total
+ {
+ get
+ {
+
+ int max = this._family == Sockets.AddressFamily.InterNetwork ? 32 : 128;
+ BigInteger count = BigInteger.Pow(2, (max - _cidr));
+ return count;
+ }
+ }
+
+
+ /// <summary>
+ /// The CIDR netmask notation
+ /// </summary>
+ public byte Cidr
+ {
+ get
+ {
+ return this._cidr;
+ }
+ }
+
+ #endregion
+
+ #region constructor
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+
+ IPNetwork(BigInteger ipaddress, AddressFamily family, byte cidr)
+ {
+
+ int maxCidr = family == Sockets.AddressFamily.InterNetwork ? 32 : 128;
+ if (cidr > maxCidr)
+ {
+ throw new ArgumentOutOfRangeException("cidr");
+ }
+
+ this._ipaddress = ipaddress;
+ this._family = family;
+ this._cidr = cidr;
+
+ }
+
+ #endregion
+
+ #region parsers
+
+ /// <summary>
+ /// 192.168.168.100 - 255.255.255.0
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static IPNetwork Parse(string ipaddress, string netmask)
+ {
+
+ IPNetwork ipnetwork = null;
+ IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork);
+ return ipnetwork;
+ }
+
+ /// <summary>
+ /// 192.168.168.100/24
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static IPNetwork Parse(string ipaddress, byte cidr)
+ {
+
+ IPNetwork ipnetwork = null;
+ IPNetwork.InternalParse(false, ipaddress, cidr, out ipnetwork);
+ return ipnetwork;
+
+ }
+
+ /// <summary>
+ /// 192.168.168.100 255.255.255.0
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static IPNetwork Parse(IPAddress ipaddress, IPAddress netmask)
+ {
+
+ IPNetwork ipnetwork = null;
+ IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork);
+ return ipnetwork;
+
+ }
+
+ /// <summary>
+ /// 192.168.0.1/24
+ /// 192.168.0.1 255.255.255.0
+ ///
+ /// Network : 192.168.0.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.0.1
+ /// End : 192.168.0.254
+ /// Broadcast : 192.168.0.255
+ /// </summary>
+ /// <param name="network"></param>
+ /// <returns></returns>
+ public static IPNetwork Parse(string network)
+ {
+
+ IPNetwork ipnetwork = null;
+ IPNetwork.InternalParse(false, network, out ipnetwork);
+ return ipnetwork;
+
+ }
+
+ #endregion
+
+ #region TryParse
+
+
+
+ /// <summary>
+ /// 192.168.168.100 - 255.255.255.0
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static bool TryParse(string ipaddress, string netmask, out IPNetwork ipnetwork)
+ {
+
+ IPNetwork ipnetwork2 = null;
+ IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2);
+ bool parsed = (ipnetwork2 != null);
+ ipnetwork = ipnetwork2;
+ return parsed;
+
+ }
+
+
+
+ /// <summary>
+ /// 192.168.168.100/24
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static bool TryParse(string ipaddress, byte cidr, out IPNetwork ipnetwork)
+ {
+
+ IPNetwork ipnetwork2 = null;
+ IPNetwork.InternalParse(true, ipaddress, cidr, out ipnetwork2);
+ bool parsed = (ipnetwork2 != null);
+ ipnetwork = ipnetwork2;
+ return parsed;
+
+ }
+
+ /// <summary>
+ /// 192.168.0.1/24
+ /// 192.168.0.1 255.255.255.0
+ ///
+ /// Network : 192.168.0.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.0.1
+ /// End : 192.168.0.254
+ /// Broadcast : 192.168.0.255
+ /// </summary>
+ /// <param name="network"></param>
+ /// <param name="ipnetwork"></param>
+ /// <returns></returns>
+ public static bool TryParse(string network, out IPNetwork ipnetwork)
+ {
+
+ IPNetwork ipnetwork2 = null;
+ IPNetwork.InternalParse(true, network, out ipnetwork2);
+ bool parsed = (ipnetwork2 != null);
+ ipnetwork = ipnetwork2;
+ return parsed;
+
+ }
+
+ /// <summary>
+ /// 192.168.0.1/24
+ /// 192.168.0.1 255.255.255.0
+ ///
+ /// Network : 192.168.0.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.0.1
+ /// End : 192.168.0.254
+ /// Broadcast : 192.168.0.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <param name="ipnetwork"></param>
+ /// <returns></returns>
+ public static bool TryParse(IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork)
+ {
+
+ IPNetwork ipnetwork2 = null;
+ IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2);
+ bool parsed = (ipnetwork2 != null);
+ ipnetwork = ipnetwork2;
+ return parsed;
+
+ }
+
+
+ #endregion
+
+ #region InternalParse
+
+ /// <summary>
+ /// 192.168.168.100 - 255.255.255.0
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ private static void InternalParse(bool tryParse, string ipaddress, string netmask, out IPNetwork ipnetwork)
+ {
+
+ if (string.IsNullOrEmpty(ipaddress))
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ if (string.IsNullOrEmpty(netmask))
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("netmask");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ IPAddress ip = null;
+ bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip);
+ if (ipaddressParsed == false)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("ipaddress");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ IPAddress mask = null;
+ bool netmaskParsed = IPAddress.TryParse(netmask, out mask);
+ if (netmaskParsed == false)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("netmask");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork);
+ }
+
+ private static void InternalParse(bool tryParse, string network, out IPNetwork ipnetwork)
+ {
+
+ if (string.IsNullOrEmpty(network))
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("network");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ network = Regex.Replace(network, @"[^0-9a-fA-F\.\/\s\:]+", "");
+ network = Regex.Replace(network, @"\s{2,}", " ");
+ network = network.Trim();
+ string[] args = network.Split(new char[] { ' ', '/' });
+ byte cidr = 0;
+ if (args.Length == 1)
+ {
+
+ if (IPNetwork.TryGuessCidr(args[0], out cidr))
+ {
+ IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork);
+ return;
+ }
+
+ if (tryParse == false)
+ {
+ throw new ArgumentException("network");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ if (byte.TryParse(args[1], out cidr))
+ {
+ IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork);
+ return;
+ }
+
+ IPNetwork.InternalParse(tryParse, args[0], args[1], out ipnetwork);
+ return;
+
+ }
+
+
+
+ /// <summary>
+ /// 192.168.168.100 255.255.255.0
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ private static void InternalParse(bool tryParse, IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork)
+ {
+
+ if (ipaddress == null)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ if (netmask == null)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("netmask");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ BigInteger uintIpAddress = IPNetwork.ToBigInteger(ipaddress);
+ byte? cidr2 = null;
+ bool parsed = IPNetwork.TryToCidr(netmask, out cidr2);
+ if (parsed == false)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("netmask");
+ }
+ ipnetwork = null;
+ return;
+ }
+ byte cidr = (byte)cidr2;
+
+ IPNetwork ipnet = new IPNetwork(uintIpAddress, ipaddress.AddressFamily, cidr);
+ ipnetwork = ipnet;
+
+ return;
+ }
+
+
+
+ /// <summary>
+ /// 192.168.168.100/24
+ ///
+ /// Network : 192.168.168.0
+ /// Netmask : 255.255.255.0
+ /// Cidr : 24
+ /// Start : 192.168.168.1
+ /// End : 192.168.168.254
+ /// Broadcast : 192.168.168.255
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ private static void InternalParse(bool tryParse, string ipaddress, byte cidr, out IPNetwork ipnetwork)
+ {
+
+ if (string.IsNullOrEmpty(ipaddress))
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+
+ IPAddress ip = null;
+ bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip);
+ if (ipaddressParsed == false)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("ipaddress");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ IPAddress mask = null;
+ bool parsedNetmask = IPNetwork.TryToNetmask(cidr, ip.AddressFamily, out mask);
+ if (parsedNetmask == false)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("cidr");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+
+ IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork);
+ }
+
+ #endregion
+
+ #region converters
+
+ #region ToUint
+
+ /// <summary>
+ /// Convert an ipadress to decimal
+ /// 0.0.0.0 -> 0
+ /// 0.0.1.0 -> 256
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <returns></returns>
+ public static BigInteger ToBigInteger(IPAddress ipaddress)
+ {
+ BigInteger? uintIpAddress = null;
+ IPNetwork.InternalToBigInteger(false, ipaddress, out uintIpAddress);
+ return (BigInteger)uintIpAddress;
+
+ }
+
+ /// <summary>
+ /// Convert an ipadress to decimal
+ /// 0.0.0.0 -> 0
+ /// 0.0.1.0 -> 256
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <returns></returns>
+ public static bool TryToBigInteger(IPAddress ipaddress, out BigInteger? uintIpAddress)
+ {
+ BigInteger? uintIpAddress2 = null;
+ IPNetwork.InternalToBigInteger(true, ipaddress, out uintIpAddress2);
+ bool parsed = (uintIpAddress2 != null);
+ uintIpAddress = uintIpAddress2;
+ return parsed;
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static void InternalToBigInteger(bool tryParse, IPAddress ipaddress, out BigInteger? uintIpAddress)
+ {
+
+ if (ipaddress == null)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+ uintIpAddress = null;
+ return;
+ }
+
+ byte[] bytes = ipaddress.GetAddressBytes();
+ /// 20180217 lduchosal
+ /// code impossible to reach, GetAddressBytes returns either 4 or 16 bytes length addresses
+ /// if (bytes.Length != 4 && bytes.Length != 16) {
+ /// if (tryParse == false) {
+ /// throw new ArgumentException("bytes");
+ /// }
+ /// uintIpAddress = null;
+ /// return;
+ /// }
+
+ Array.Reverse(bytes);
+ var unsigned = new List<byte>(bytes);
+ unsigned.Add(0);
+ uintIpAddress = new BigInteger(unsigned.ToArray());
+ return;
+ }
+
+
+ /// <summary>
+ /// Convert a cidr to BigInteger netmask
+ /// </summary>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static BigInteger ToUint(byte cidr, AddressFamily family)
+ {
+
+ BigInteger? uintNetmask = null;
+ IPNetwork.InternalToBigInteger(false, cidr, family, out uintNetmask);
+ return (BigInteger)uintNetmask;
+ }
+
+
+ /// <summary>
+ /// Convert a cidr to uint netmask
+ /// </summary>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static bool TryToUint(byte cidr, AddressFamily family, out BigInteger? uintNetmask)
+ {
+
+ BigInteger? uintNetmask2 = null;
+ IPNetwork.InternalToBigInteger(true, cidr, family, out uintNetmask2);
+ bool parsed = (uintNetmask2 != null);
+ uintNetmask = uintNetmask2;
+ return parsed;
+ }
+
+ /// <summary>
+ /// Convert a cidr to uint netmask
+ /// </summary>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static void InternalToBigInteger(bool tryParse, byte cidr, AddressFamily family, out BigInteger? uintNetmask)
+ {
+
+ if (family == AddressFamily.InterNetwork && cidr > 32)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentOutOfRangeException("cidr");
+ }
+ uintNetmask = null;
+ return;
+ }
+
+ if (family == AddressFamily.InterNetworkV6 && cidr > 128)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentOutOfRangeException("cidr");
+ }
+ uintNetmask = null;
+ return;
+ }
+
+ if (family != AddressFamily.InterNetwork
+ && family != AddressFamily.InterNetworkV6)
+ {
+ if (tryParse == false)
+ {
+ throw new NotSupportedException(family.ToString());
+ }
+ uintNetmask = null;
+ return;
+ }
+
+ if (family == AddressFamily.InterNetwork)
+ {
+
+ uintNetmask = cidr == 0 ? 0 : 0xffffffff << (32 - cidr);
+ return;
+ }
+
+ BigInteger mask = new BigInteger(new byte[] {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0x00
+ });
+
+ BigInteger masked = cidr == 0 ? 0 : mask << (128 - cidr);
+ byte[] m = masked.ToByteArray();
+ byte[] bmask = new byte[17];
+ int copy = m.Length > 16 ? 16 : m.Length;
+ Array.Copy(m, 0, bmask, 0, copy);
+ uintNetmask = new BigInteger(bmask);
+
+
+ }
+
+ #endregion
+
+ #region ToCidr
+
+ /// <summary>
+ /// Convert netmask to CIDR
+ /// 255.255.255.0 -> 24
+ /// 255.255.0.0 -> 16
+ /// 255.0.0.0 -> 8
+ /// </summary>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ private static void InternalToCidr(bool tryParse, BigInteger netmask, AddressFamily family, out byte? cidr)
+ {
+
+ if (!IPNetwork.InternalValidNetmask(netmask, family))
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("netmask");
+ }
+ cidr = null;
+ return;
+ }
+
+ byte cidr2 = IPNetwork.BitsSet(netmask, family);
+ cidr = cidr2;
+ return;
+
+ }
+ /// <summary>
+ /// Convert netmask to CIDR
+ /// 255.255.255.0 -> 24
+ /// 255.255.0.0 -> 16
+ /// 255.0.0.0 -> 8
+ /// </summary>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static byte ToCidr(IPAddress netmask)
+ {
+ byte? cidr = null;
+ IPNetwork.InternalToCidr(false, netmask, out cidr);
+ return (byte)cidr;
+ }
+
+ /// <summary>
+ /// Convert netmask to CIDR
+ /// 255.255.255.0 -> 24
+ /// 255.255.0.0 -> 16
+ /// 255.0.0.0 -> 8
+ /// </summary>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static bool TryToCidr(IPAddress netmask, out byte? cidr)
+ {
+ byte? cidr2 = null;
+ IPNetwork.InternalToCidr(true, netmask, out cidr2);
+ bool parsed = (cidr2 != null);
+ cidr = cidr2;
+ return parsed;
+ }
+
+ private static void InternalToCidr(bool tryParse, IPAddress netmask, out byte? cidr)
+ {
+
+ if (netmask == null)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentNullException("netmask");
+ }
+ cidr = null;
+ return;
+ }
+ BigInteger? uintNetmask2 = null;
+ bool parsed = IPNetwork.TryToBigInteger(netmask, out uintNetmask2);
+
+ /// 20180217 lduchosal
+ /// impossible to reach code.
+ /// if (parsed == false) {
+ /// if (tryParse == false) {
+ /// throw new ArgumentException("netmask");
+ /// }
+ /// cidr = null;
+ /// return;
+ /// }
+ BigInteger uintNetmask = (BigInteger)uintNetmask2;
+
+ byte? cidr2 = null;
+ IPNetwork.InternalToCidr(tryParse, uintNetmask, netmask.AddressFamily, out cidr2);
+ cidr = cidr2;
+
+ return;
+
+ }
+
+
+ #endregion
+
+ #region ToNetmask
+
+ /// <summary>
+ /// Convert CIDR to netmask
+ /// 24 -> 255.255.255.0
+ /// 16 -> 255.255.0.0
+ /// 8 -> 255.0.0.0
+ /// </summary>
+ /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static IPAddress ToNetmask(byte cidr, AddressFamily family)
+ {
+
+ IPAddress netmask = null;
+ IPNetwork.InternalToNetmask(false, cidr, family, out netmask);
+ return netmask;
+ }
+
+ /// <summary>
+ /// Convert CIDR to netmask
+ /// 24 -> 255.255.255.0
+ /// 16 -> 255.255.0.0
+ /// 8 -> 255.0.0.0
+ /// </summary>
+ /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static bool TryToNetmask(byte cidr, AddressFamily family, out IPAddress netmask)
+ {
+
+ IPAddress netmask2 = null;
+ IPNetwork.InternalToNetmask(true, cidr, family, out netmask2);
+ bool parsed = (netmask2 != null);
+ netmask = netmask2;
+ return parsed;
+ }
+
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static void InternalToNetmask(bool tryParse, byte cidr, AddressFamily family, out IPAddress netmask)
+ {
+
+ if (family != AddressFamily.InterNetwork
+ && family != AddressFamily.InterNetworkV6)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentException("family");
+ }
+ netmask = null;
+ return;
+ }
+
+ /// 20180217 lduchosal
+ /// impossible to reach code, byte cannot be negative :
+ ///
+ /// if (cidr < 0) {
+ /// if (tryParse == false) {
+ /// throw new ArgumentOutOfRangeException("cidr");
+ /// }
+ /// netmask = null;
+ /// return;
+ /// }
+
+ int maxCidr = family == Sockets.AddressFamily.InterNetwork ? 32 : 128;
+ if (cidr > maxCidr)
+ {
+ if (tryParse == false)
+ {
+ throw new ArgumentOutOfRangeException("cidr");
+ }
+ netmask = null;
+ return;
+ }
+
+ BigInteger mask = IPNetwork.ToUint(cidr, family);
+ IPAddress netmask2 = IPNetwork.ToIPAddress(mask, family);
+ netmask = netmask2;
+
+ return;
+ }
+
+ #endregion
+
+ #endregion
+
+ #region utils
+
+ #region BitsSet
+
+ /// <summary>
+ /// Count bits set to 1 in netmask
+ /// </summary>
+ /// <see cref="http://stackoverflow.com/questions/109023/best-algorithm-to-count-the-number-of-set-bits-in-a-32-bit-integer"/>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ private static byte BitsSet(BigInteger netmask, AddressFamily family)
+ {
+
+ string s = netmask.ToBinaryString();
+ return (byte)s.Replace("0", "")
+ .ToCharArray()
+ .Length;
+
+ }
+
+
+ /// <summary>
+ /// Count bits set to 1 in netmask
+ /// </summary>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static uint BitsSet(IPAddress netmask)
+ {
+ BigInteger uintNetmask = IPNetwork.ToBigInteger(netmask);
+ uint bits = IPNetwork.BitsSet(uintNetmask, netmask.AddressFamily);
+ return bits;
+ }
+
+ #endregion
+
+ #region ValidNetmask
+
+ /// <summary>
+ /// return true if netmask is a valid netmask
+ /// 255.255.255.0, 255.0.0.0, 255.255.240.0, ...
+ /// </summary>
+ /// <see cref="http://www.actionsnip.com/snippets/tomo_atlacatl/calculate-if-a-netmask-is-valid--as2-"/>
+ /// <param name="netmask"></param>
+ /// <returns></returns>
+ public static bool ValidNetmask(IPAddress netmask)
+ {
+
+ if (netmask == null)
+ {
+ throw new ArgumentNullException("netmask");
+ }
+ BigInteger uintNetmask = IPNetwork.ToBigInteger(netmask);
+ bool valid = IPNetwork.InternalValidNetmask(uintNetmask, netmask.AddressFamily);
+ return valid;
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static bool InternalValidNetmask(BigInteger netmask, AddressFamily family)
+ {
+
+ if (family != AddressFamily.InterNetwork
+ && family != AddressFamily.InterNetworkV6)
+ {
+ throw new ArgumentException("family");
+ }
+
+ var mask = family == AddressFamily.InterNetwork
+ ? new BigInteger(0x0ffffffff)
+ : new BigInteger(new byte[]{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0x00
+ });
+
+ BigInteger neg = ((~netmask) & (mask));
+ bool isNetmask = ((neg + 1) & neg) == 0;
+ return isNetmask;
+
+ }
+
+ #endregion
+
+ #region ToIPAddress
+
+ /// <summary>
+ /// Transform a uint ipaddress into IPAddress object
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <returns></returns>
+ public static IPAddress ToIPAddress(BigInteger ipaddress, AddressFamily family)
+ {
+
+ int width = family == AddressFamily.InterNetwork ? 4 : 16;
+ byte[] bytes = ipaddress.ToByteArray();
+ byte[] bytes2 = new byte[width];
+ int copy = bytes.Length > width ? width : bytes.Length;
+ Array.Copy(bytes, 0, bytes2, 0, copy);
+ Array.Reverse(bytes2);
+
+ byte[] sized = Resize(bytes2, family);
+ IPAddress ip = new IPAddress(sized);
+ return ip;
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static byte[] Resize(byte[] bytes, AddressFamily family)
+ {
+
+ if (family != AddressFamily.InterNetwork
+ && family != AddressFamily.InterNetworkV6)
+ {
+ throw new ArgumentException("family");
+ }
+
+ int width = family == AddressFamily.InterNetwork ? 4 : 16;
+
+ if (bytes.Length > width)
+ {
+ throw new ArgumentException("bytes");
+ }
+
+ byte[] result = new byte[width];
+ Array.Copy(bytes, 0, result, 0, bytes.Length);
+ return result;
+ }
+
+ #endregion
+
+ #endregion
+
+ #region contains
+
+ /// <summary>
+ /// return true if ipaddress is contained in network
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <returns></returns>
+ public bool Contains(IPAddress ipaddress)
+ {
+
+ if (ipaddress == null)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+
+ if (AddressFamily != ipaddress.AddressFamily)
+ {
+ return false;
+ }
+
+ BigInteger uintNetwork = _network;
+ BigInteger uintBroadcast = _broadcast;
+ BigInteger uintAddress = IPNetwork.ToBigInteger(ipaddress);
+
+ bool contains = (uintAddress >= uintNetwork
+ && uintAddress <= uintBroadcast);
+
+ return contains;
+
+ }
+
+ [Obsolete("static Contains is deprecated, please use instance Contains.")]
+ public static bool Contains(IPNetwork network, IPAddress ipaddress)
+ {
+
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+
+ return network.Contains(ipaddress);
+ }
+
+ /// <summary>
+ /// return true is network2 is fully contained in network
+ /// </summary>
+ /// <param name="network2"></param>
+ /// <returns></returns>
+ public bool Contains(IPNetwork network2)
+ {
+
+ if (network2 == null)
+ {
+ throw new ArgumentNullException("network2");
+ }
+
+ BigInteger uintNetwork = _network;
+ BigInteger uintBroadcast = _broadcast;
+
+ BigInteger uintFirst = network2._network;
+ BigInteger uintLast = network2._broadcast;
+
+ bool contains = (uintFirst >= uintNetwork
+ && uintLast <= uintBroadcast);
+
+ return contains;
+ }
+
+ [Obsolete("static Contains is deprecated, please use instance Contains.")]
+ public static bool Contains(IPNetwork network, IPNetwork network2)
+ {
+
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+
+ return network.Contains(network2);
+ }
+
+ #endregion
+
+ #region overlap
+
+ /// <summary>
+ /// return true is network2 overlap network
+ /// </summary>
+ /// <param name="network2"></param>
+ /// <returns></returns>
+ public bool Overlap(IPNetwork network2)
+ {
+
+ if (network2 == null)
+ {
+ throw new ArgumentNullException("network2");
+ }
+
+ BigInteger uintNetwork = _network;
+ BigInteger uintBroadcast = _broadcast;
+
+ BigInteger uintFirst = network2._network;
+ BigInteger uintLast = network2._broadcast;
+
+ bool overlap =
+ (uintFirst >= uintNetwork && uintFirst <= uintBroadcast)
+ || (uintLast >= uintNetwork && uintLast <= uintBroadcast)
+ || (uintFirst <= uintNetwork && uintLast >= uintBroadcast)
+ || (uintFirst >= uintNetwork && uintLast <= uintBroadcast);
+
+ return overlap;
+ }
+
+ [Obsolete("static Overlap is deprecated, please use instance Overlap.")]
+ public static bool Overlap(IPNetwork network, IPNetwork network2)
+ {
+
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+
+ return network.Overlap(network2);
+ }
+
+ #endregion
+
+ #region ToString
+
+ public override string ToString()
+ {
+ return string.Format("{0}/{1}", this.Network, this.Cidr);
+ }
+
+ #endregion
+
+ #region IANA block
+
+ private static readonly Lazy<IPNetwork> _iana_ablock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("10.0.0.0/8"));
+ private static readonly Lazy<IPNetwork> _iana_bblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("172.16.0.0/12"));
+ private static readonly Lazy<IPNetwork> _iana_cblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("192.168.0.0/16"));
+
+ /// <summary>
+ /// 10.0.0.0/8
+ /// </summary>
+ /// <returns></returns>
+ public static IPNetwork IANA_ABLK_RESERVED1
+ {
+ get
+ {
+ return _iana_ablock_reserved.Value;
+ }
+ }
+
+ /// <summary>
+ /// 172.12.0.0/12
+ /// </summary>
+ /// <returns></returns>
+ public static IPNetwork IANA_BBLK_RESERVED1
+ {
+ get
+ {
+ return _iana_bblock_reserved.Value;
+ }
+ }
+
+ /// <summary>
+ /// 192.168.0.0/16
+ /// </summary>
+ /// <returns></returns>
+ public static IPNetwork IANA_CBLK_RESERVED1
+ {
+ get
+ {
+ return _iana_cblock_reserved.Value;
+ }
+ }
+
+ /// <summary>
+ /// return true if ipaddress is contained in
+ /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1
+ /// </summary>
+ /// <param name="ipaddress"></param>
+ /// <returns></returns>
+ public static bool IsIANAReserved(IPAddress ipaddress)
+ {
+
+ if (ipaddress == null)
+ {
+ throw new ArgumentNullException("ipaddress");
+ }
+
+ return IPNetwork.IANA_ABLK_RESERVED1.Contains(ipaddress)
+ || IPNetwork.IANA_BBLK_RESERVED1.Contains(ipaddress)
+ || IPNetwork.IANA_CBLK_RESERVED1.Contains(ipaddress);
+ }
+
+ /// <summary>
+ /// return true if ipnetwork is contained in
+ /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1
+ /// </summary>
+ /// <returns></returns>
+ public bool IsIANAReserved()
+ {
+ return IPNetwork.IANA_ABLK_RESERVED1.Contains(this)
+ || IPNetwork.IANA_BBLK_RESERVED1.Contains(this)
+ || IPNetwork.IANA_CBLK_RESERVED1.Contains(this);
+ }
+
+ [Obsolete("static IsIANAReserved is deprecated, please use instance IsIANAReserved.")]
+ public static bool IsIANAReserved(IPNetwork ipnetwork)
+ {
+
+ if (ipnetwork == null)
+ {
+ throw new ArgumentNullException("ipnetwork");
+ }
+
+ return ipnetwork.IsIANAReserved();
+ }
+
+ #endregion
+
+ #region Subnet
+
+ /// <summary>
+ /// Subnet a network into multiple nets of cidr mask
+ /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25
+ /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9
+ /// </summary>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public IPNetworkCollection Subnet(byte cidr)
+ {
+ IPNetworkCollection ipnetworkCollection = null;
+ IPNetwork.InternalSubnet(false, this, cidr, out ipnetworkCollection);
+ return ipnetworkCollection;
+ }
+
+ [Obsolete("static Subnet is deprecated, please use instance Subnet.")]
+ public static IPNetworkCollection Subnet(IPNetwork network, byte cidr)
+ {
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+ return network.Subnet(cidr);
+ }
+
+ /// <summary>
+ /// Subnet a network into multiple nets of cidr mask
+ /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25
+ /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9
+ /// </summary>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public bool TrySubnet(byte cidr, out IPNetworkCollection ipnetworkCollection)
+ {
+ IPNetworkCollection inc = null;
+ IPNetwork.InternalSubnet(true, this, cidr, out inc);
+ if (inc == null)
+ {
+ ipnetworkCollection = null;
+ return false;
+ }
+
+ ipnetworkCollection = inc;
+ return true;
+ }
+
+ [Obsolete("static TrySubnet is deprecated, please use instance TrySubnet.")]
+ public static bool TrySubnet(IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection)
+ {
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+ return network.TrySubnet(cidr, out ipnetworkCollection);
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static void InternalSubnet(bool trySubnet, IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection)
+ {
+
+ if (network == null)
+ {
+ if (trySubnet == false)
+ {
+ throw new ArgumentNullException("network");
+ }
+ ipnetworkCollection = null;
+ return;
+ }
+
+ int maxCidr = network._family == Sockets.AddressFamily.InterNetwork ? 32 : 128;
+ if (cidr > maxCidr)
+ {
+ if (trySubnet == false)
+ {
+ throw new ArgumentOutOfRangeException("cidr");
+ }
+ ipnetworkCollection = null;
+ return;
+ }
+
+ if (cidr < network.Cidr)
+ {
+ if (trySubnet == false)
+ {
+ throw new ArgumentException("cidr");
+ }
+ ipnetworkCollection = null;
+ return;
+ }
+
+ ipnetworkCollection = new IPNetworkCollection(network, cidr);
+ return;
+ }
+
+
+
+ #endregion
+
+ #region Supernet
+
+ /// <summary>
+ /// Supernet two consecutive cidr equal subnet into a single one
+ /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
+ /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15
+ /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24
+ /// </summary>
+ /// <param name="network2"></param>
+ /// <returns></returns>
+ public IPNetwork Supernet(IPNetwork network2)
+ {
+ IPNetwork supernet = null;
+ IPNetwork.InternalSupernet(false, this, network2, out supernet);
+ return supernet;
+ }
+
+ [Obsolete("static Supernet is deprecated, please use instance Supernet.")]
+ public static IPNetwork Supernet(IPNetwork network, IPNetwork network2)
+ {
+ return network.Supernet(network2);
+ }
+
+ /// <summary>
+ /// Try to supernet two consecutive cidr equal subnet into a single one
+ /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
+ /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15
+ /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24
+ /// </summary>
+ /// <param name="network2"></param>
+ /// <returns></returns>
+ public bool TrySupernet(IPNetwork network2, out IPNetwork supernet)
+ {
+
+ IPNetwork outSupernet = null;
+ IPNetwork.InternalSupernet(true, this, network2, out outSupernet);
+ bool parsed = (outSupernet != null);
+ supernet = outSupernet;
+ return parsed;
+ }
+
+ [Obsolete("static TrySupernet is deprecated, please use instance TrySupernet.")]
+ public static bool TrySupernet(IPNetwork network, IPNetwork network2, out IPNetwork supernet)
+ {
+ if (network == null)
+ {
+ throw new ArgumentNullException("network");
+ }
+ return network.TrySupernet(network2, out supernet);
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static void InternalSupernet(bool trySupernet, IPNetwork network1, IPNetwork network2, out IPNetwork supernet)
+ {
+
+ if (network1 == null)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentNullException("network1");
+ }
+ supernet = null;
+ return;
+ }
+
+ if (network2 == null)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentNullException("network2");
+ }
+ supernet = null;
+ return;
+ }
+
+
+ if (network1.Contains(network2))
+ {
+ supernet = new IPNetwork(network1._network, network1._family, network1.Cidr);
+ return;
+ }
+
+ if (network2.Contains(network1))
+ {
+ supernet = new IPNetwork(network2._network, network2._family, network2.Cidr);
+ return;
+ }
+
+ if (network1._cidr != network2._cidr)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentException("cidr");
+ }
+ supernet = null;
+ return;
+ }
+
+ IPNetwork first = (network1._network < network2._network) ? network1 : network2;
+ IPNetwork last = (network1._network > network2._network) ? network1 : network2;
+
+ /// Starting from here :
+ /// network1 and network2 have the same cidr,
+ /// network1 does not contain network2,
+ /// network2 does not contain network1,
+ /// first is the lower subnet
+ /// last is the higher subnet
+
+
+ if ((first._broadcast + 1) != last._network)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentOutOfRangeException("network");
+ }
+ supernet = null;
+ return;
+ }
+
+ BigInteger uintSupernet = first._network;
+ byte cidrSupernet = (byte)(first._cidr - 1);
+
+ IPNetwork networkSupernet = new IPNetwork(uintSupernet, first._family, cidrSupernet);
+ if (networkSupernet._network != first._network)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentException("network");
+ }
+ supernet = null;
+ return;
+ }
+ supernet = networkSupernet;
+ return;
+ }
+
+ #endregion
+
+ #region GetHashCode
+
+ public override int GetHashCode()
+ {
+ return string.Format("{0}|{1}|{2}",
+ this._ipaddress.GetHashCode(),
+ this._network.GetHashCode(),
+ this._cidr.GetHashCode()).GetHashCode();
+ }
+
+ #endregion
+
+ #region SupernetArray
+
+ /// <summary>
+ /// Supernet a list of subnet
+ /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
+ /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22
+ /// </summary>
+ /// <param name="ipnetworks"></param>
+ /// <param name="supernet"></param>
+ /// <returns></returns>
+ public static IPNetwork[] Supernet(IPNetwork[] ipnetworks)
+ {
+ IPNetwork[] supernet;
+ InternalSupernet(false, ipnetworks, out supernet);
+ return supernet;
+ }
+
+ /// <summary>
+ /// Supernet a list of subnet
+ /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
+ /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22
+ /// </summary>
+ /// <param name="ipnetworks"></param>
+ /// <param name="supernet"></param>
+ /// <returns></returns>
+ public static bool TrySupernet(IPNetwork[] ipnetworks, out IPNetwork[] supernet)
+ {
+ bool supernetted = InternalSupernet(true, ipnetworks, out supernet);
+ return supernetted;
+
+ }
+
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ static bool InternalSupernet(bool trySupernet, IPNetwork[] ipnetworks, out IPNetwork[] supernet)
+ {
+
+ if (ipnetworks == null)
+ {
+ if (trySupernet == false)
+ {
+ throw new ArgumentNullException("ipnetworks");
+ }
+ supernet = null;
+ return false;
+ }
+
+ if (ipnetworks.Length <= 0)
+ {
+ supernet = new IPNetwork[0];
+ return true;
+ }
+
+ List<IPNetwork> supernetted = new List<IPNetwork>();
+ List<IPNetwork> ipns = IPNetwork.Array2List(ipnetworks);
+ Stack<IPNetwork> current = IPNetwork.List2Stack(ipns);
+ int previousCount = 0;
+ int currentCount = current.Count;
+
+ while (previousCount != currentCount)
+ {
+
+ supernetted.Clear();
+ while (current.Count > 1)
+ {
+ IPNetwork ipn1 = current.Pop();
+ IPNetwork ipn2 = current.Peek();
+
+ IPNetwork outNetwork = null;
+ bool success = ipn1.TrySupernet(ipn2, out outNetwork);
+ if (success)
+ {
+ current.Pop();
+ current.Push(outNetwork);
+ }
+ else
+ {
+ supernetted.Add(ipn1);
+ }
+ }
+ if (current.Count == 1)
+ {
+ supernetted.Add(current.Pop());
+ }
+
+ previousCount = currentCount;
+ currentCount = supernetted.Count;
+ current = IPNetwork.List2Stack(supernetted);
+
+ }
+ supernet = supernetted.ToArray();
+ return true;
+ }
+
+ private static Stack<IPNetwork> List2Stack(List<IPNetwork> list)
+ {
+ Stack<IPNetwork> stack = new Stack<IPNetwork>();
+ list.ForEach(new Action<IPNetwork>(
+ delegate (IPNetwork ipn)
+ {
+ stack.Push(ipn);
+ }
+ ));
+ return stack;
+ }
+
+ private static List<IPNetwork> Array2List(IPNetwork[] array)
+ {
+ List<IPNetwork> ipns = new List<IPNetwork>();
+ ipns.AddRange(array);
+ IPNetwork.RemoveNull(ipns);
+ ipns.Sort(new Comparison<IPNetwork>(
+ delegate (IPNetwork ipn1, IPNetwork ipn2)
+ {
+ int networkCompare = ipn1._network.CompareTo(ipn2._network);
+ if (networkCompare == 0)
+ {
+ int cidrCompare = ipn1._cidr.CompareTo(ipn2._cidr);
+ return cidrCompare;
+ }
+ return networkCompare;
+ }
+ ));
+ ipns.Reverse();
+
+ return ipns;
+ }
+
+ private static void RemoveNull(List<IPNetwork> ipns)
+ {
+ ipns.RemoveAll(new Predicate<IPNetwork>(
+ delegate (IPNetwork ipn)
+ {
+ if (ipn == null)
+ {
+ return true;
+ }
+ return false;
+ }
+ ));
+
+ }
+
+ #endregion
+
+ #region WideSubnet
+
+ public static IPNetwork WideSubnet(string start, string end)
+ {
+
+ if (string.IsNullOrEmpty(start))
+ {
+ throw new ArgumentNullException("start");
+ }
+
+ if (string.IsNullOrEmpty(end))
+ {
+ throw new ArgumentNullException("end");
+ }
+
+ IPAddress startIP;
+ if (!IPAddress.TryParse(start, out startIP))
+ {
+ throw new ArgumentException("start");
+ }
+
+ IPAddress endIP;
+ if (!IPAddress.TryParse(end, out endIP))
+ {
+ throw new ArgumentException("end");
+ }
+
+ if (startIP.AddressFamily != endIP.AddressFamily)
+ {
+ throw new NotSupportedException("MixedAddressFamily");
+ }
+
+ IPNetwork ipnetwork = new IPNetwork(0, startIP.AddressFamily, 0);
+ for (byte cidr = 32; cidr >= 0; cidr--)
+ {
+ IPNetwork wideSubnet = IPNetwork.Parse(start, cidr);
+ if (wideSubnet.Contains(endIP))
+ {
+ ipnetwork = wideSubnet;
+ break;
+ }
+ }
+ return ipnetwork;
+
+ }
+
+ public static bool TryWideSubnet(IPNetwork[] ipnetworks, out IPNetwork ipnetwork)
+ {
+ IPNetwork ipn = null;
+ IPNetwork.InternalWideSubnet(true, ipnetworks, out ipn);
+ if (ipn == null)
+ {
+ ipnetwork = null;
+ return false;
+ }
+ ipnetwork = ipn;
+ return true;
+ }
+
+ public static IPNetwork WideSubnet(IPNetwork[] ipnetworks)
+ {
+ IPNetwork ipn = null;
+ IPNetwork.InternalWideSubnet(false, ipnetworks, out ipn);
+ return ipn;
+ }
+
+ internal static void InternalWideSubnet(bool tryWide, IPNetwork[] ipnetworks, out IPNetwork ipnetwork)
+ {
+
+ if (ipnetworks == null)
+ {
+ if (tryWide == false)
+ {
+ throw new ArgumentNullException("ipnetworks");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+
+ IPNetwork[] nnin = Array.FindAll<IPNetwork>(ipnetworks, new Predicate<IPNetwork>(
+ delegate (IPNetwork ipnet) {
+ return ipnet != null;
+ }
+ ));
+
+ if (nnin.Length <= 0)
+ {
+ if (tryWide == false)
+ {
+ throw new ArgumentException("ipnetworks");
+ }
+ ipnetwork = null;
+ return;
+ }
+
+ if (nnin.Length == 1)
+ {
+ IPNetwork ipn0 = nnin[0];
+ ipnetwork = ipn0;
+ return;
+ }
+
+ Array.Sort<IPNetwork>(nnin);
+ IPNetwork nnin0 = nnin[0];
+ BigInteger uintNnin0 = nnin0._ipaddress;
+
+ IPNetwork nninX = nnin[nnin.Length - 1];
+ IPAddress ipaddressX = nninX.Broadcast;
+
+ AddressFamily family = ipnetworks[0]._family;
+ foreach (var ipnx in ipnetworks)
+ {
+ if (ipnx._family != family)
+ {
+ throw new ArgumentException("MixedAddressFamily");
+ }
+ }
+
+ IPNetwork ipn = new IPNetwork(0, family, 0);
+ for (byte cidr = nnin0._cidr; cidr >= 0; cidr--)
+ {
+ IPNetwork wideSubnet = new IPNetwork(uintNnin0, family, cidr);
+ if (wideSubnet.Contains(ipaddressX))
+ {
+ ipn = wideSubnet;
+ break;
+ }
+ }
+
+ ipnetwork = ipn;
+ return;
+ }
+
+ #endregion
+
+ #region Print
+
+ /// <summary>
+ /// Print an ipnetwork in a clear representation string
+ /// </summary>
+ /// <returns></returns>
+ public string Print()
+ {
+
+ StringWriter sw = new StringWriter();
+
+ sw.WriteLine("IPNetwork : {0}", ToString());
+ sw.WriteLine("Network : {0}", Network);
+ sw.WriteLine("Netmask : {0}", Netmask);
+ sw.WriteLine("Cidr : {0}", Cidr);
+ sw.WriteLine("Broadcast : {0}", Broadcast);
+ sw.WriteLine("FirstUsable : {0}", FirstUsable);
+ sw.WriteLine("LastUsable : {0}", LastUsable);
+ sw.WriteLine("Usable : {0}", Usable);
+
+ return sw.ToString();
+ }
+
+ [Obsolete("static Print is deprecated, please use instance Print.")]
+ public static string Print(IPNetwork ipnetwork)
+ {
+
+ if (ipnetwork == null)
+ {
+ throw new ArgumentNullException("ipnetwork");
+ }
+
+ return ipnetwork.Print();
+ }
+
+ #endregion
+
+ #region TryGuessCidr
+
+ /// <summary>
+ ///
+ /// Class Leading bits Default netmask
+ /// A (CIDR /8) 00 255.0.0.0
+ /// A (CIDR /8) 01 255.0.0.0
+ /// B (CIDR /16) 10 255.255.0.0
+ /// C (CIDR /24) 11 255.255.255.0
+ ///
+ /// </summary>
+ /// <param name="ip"></param>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static bool TryGuessCidr(string ip, out byte cidr)
+ {
+
+ IPAddress ipaddress = null;
+ bool parsed = IPAddress.TryParse(string.Format("{0}", ip), out ipaddress);
+ if (parsed == false)
+ {
+ cidr = 0;
+ return false;
+ }
+
+ if (ipaddress.AddressFamily == AddressFamily.InterNetworkV6)
+ {
+ cidr = 64;
+ return true;
+ }
+ BigInteger uintIPAddress = IPNetwork.ToBigInteger(ipaddress);
+ uintIPAddress = uintIPAddress >> 29;
+ if (uintIPAddress <= 3)
+ {
+ cidr = 8;
+ return true;
+ }
+ else if (uintIPAddress <= 5)
+ {
+ cidr = 16;
+ return true;
+ }
+ else if (uintIPAddress <= 6)
+ {
+ cidr = 24;
+ return true;
+ }
+
+ cidr = 0;
+ return false;
+
+ }
+
+ /// <summary>
+ /// Try to parse cidr. Have to be >= 0 and <= 32 or 128
+ /// </summary>
+ /// <param name="sidr"></param>
+ /// <param name="cidr"></param>
+ /// <returns></returns>
+ public static bool TryParseCidr(string sidr, AddressFamily family, out byte? cidr)
+ {
+
+ byte b = 0;
+ if (!byte.TryParse(sidr, out b))
+ {
+ cidr = null;
+ return false;
+ }
+
+ IPAddress netmask = null;
+ if (!IPNetwork.TryToNetmask(b, family, out netmask))
+ {
+ cidr = null;
+ return false;
+ }
+
+ cidr = b;
+ return true;
+ }
+
+ #endregion
+
+ #region ListIPAddress
+
+ [Obsolete("static ListIPAddress is deprecated, please use instance ListIPAddress.")]
+ public static IPAddressCollection ListIPAddress(IPNetwork ipnetwork)
+ {
+ return ipnetwork.ListIPAddress();
+ }
+
+ public IPAddressCollection ListIPAddress()
+ {
+ return new IPAddressCollection(this);
+ }
+
+ #endregion
+
+ /**
+ * Need a better way to do it
+ *
+#region TrySubstractNetwork
+
+ public static bool TrySubstractNetwork(IPNetwork[] ipnetworks, IPNetwork substract, out IEnumerable<IPNetwork> result) {
+
+ if (ipnetworks == null) {
+ result = null;
+ return false;
+ }
+ if (ipnetworks.Length <= 0) {
+ result = null;
+ return false;
+ }
+ if (substract == null) {
+ result = null;
+ return false;
+ }
+ var results = new List<IPNetwork>();
+ foreach (var ipn in ipnetworks) {
+ if (!Overlap(ipn, substract)) {
+ results.Add(ipn);
+ continue;
+ }
+
+ var collection = ipn.Subnet(substract.Cidr);
+ var rtemp = new List<IPNetwork>();
+ foreach(var subnet in collection) {
+ if (subnet != substract) {
+ rtemp.Add(subnet);
+ }
+ }
+ var supernets = Supernet(rtemp.ToArray());
+ results.AddRange(supernets);
+ }
+ result = results;
+ return true;
+ }
+#endregion
+ * **/
+
+ #region IComparable<IPNetwork> Members
+
+ public static Int32 Compare(IPNetwork left, IPNetwork right)
+ {
+ // two null IPNetworks are equal
+ if (ReferenceEquals(left, null) && ReferenceEquals(right, null)) return 0;
+
+ // two same IPNetworks are equal
+ if (ReferenceEquals(left, right)) return 0;
+
+ // null is always sorted first
+ if (ReferenceEquals(left, null)) return -1;
+ if (ReferenceEquals(right, null)) return 1;
+
+ // first test the network
+ var result = left._network.CompareTo(right._network);
+ if (result != 0) return result;
+
+ // then test the cidr
+ result = left._cidr.CompareTo(right._cidr);
+ return result;
+ }
+
+ public Int32 CompareTo(IPNetwork other)
+ {
+ return Compare(this, other);
+ }
+
+ public Int32 CompareTo(Object obj)
+ {
+ // null is at less
+ if (obj == null) return 1;
+
+ // convert to a proper Cidr object
+ var other = obj as IPNetwork;
+
+ // type problem if null
+ if (other == null)
+ {
+ throw new ArgumentException(
+ "The supplied parameter is an invalid type. Please supply an IPNetwork type.",
+ "obj");
+ }
+
+ // perform the comparision
+ return CompareTo(other);
+ }
+
+ #endregion
+
+ #region IEquatable<IPNetwork> Members
+
+ public static Boolean Equals(IPNetwork left, IPNetwork right)
+ {
+ return Compare(left, right) == 0;
+ }
+
+ public Boolean Equals(IPNetwork other)
+ {
+ return Equals(this, other);
+ }
+
+ public override Boolean Equals(Object obj)
+ {
+ return Equals(this, obj as IPNetwork);
+ }
+
+ #endregion
+
+ #region Operators
+
+ public static Boolean operator ==(IPNetwork left, IPNetwork right)
+ {
+ return Equals(left, right);
+ }
+
+ public static Boolean operator !=(IPNetwork left, IPNetwork right)
+ {
+ return !Equals(left, right);
+ }
+
+ public static Boolean operator <(IPNetwork left, IPNetwork right)
+ {
+ return Compare(left, right) < 0;
+ }
+
+ public static Boolean operator >(IPNetwork left, IPNetwork right)
+ {
+ return Compare(left, right) > 0;
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs
new file mode 100644
index 000000000..35cff88dc
--- /dev/null
+++ b/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs
@@ -0,0 +1,144 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace System.Net
+{
+ public class IPNetworkCollection : IEnumerable<IPNetwork>, IEnumerator<IPNetwork>
+ {
+
+ private BigInteger _enumerator;
+ private byte _cidrSubnet;
+ private IPNetwork _ipnetwork;
+
+ private byte _cidr
+ {
+ get { return this._ipnetwork.Cidr; }
+ }
+ private BigInteger _broadcast
+ {
+ get { return IPNetwork.ToBigInteger(this._ipnetwork.Broadcast); }
+ }
+ private BigInteger _lastUsable
+ {
+ get { return IPNetwork.ToBigInteger(this._ipnetwork.LastUsable); }
+ }
+ private BigInteger _network
+ {
+ get { return IPNetwork.ToBigInteger(this._ipnetwork.Network); }
+ }
+#if TRAVISCI
+ public
+#else
+ internal
+#endif
+ IPNetworkCollection(IPNetwork ipnetwork, byte cidrSubnet)
+ {
+
+ int maxCidr = ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetwork ? 32 : 128;
+ if (cidrSubnet > maxCidr)
+ {
+ throw new ArgumentOutOfRangeException("cidrSubnet");
+ }
+
+ if (cidrSubnet < ipnetwork.Cidr)
+ {
+ throw new ArgumentException("cidr");
+ }
+
+ this._cidrSubnet = cidrSubnet;
+ this._ipnetwork = ipnetwork;
+ this._enumerator = -1;
+ }
+
+ #region Count, Array, Enumerator
+
+ public BigInteger Count
+ {
+ get
+ {
+ BigInteger count = BigInteger.Pow(2, this._cidrSubnet - this._cidr);
+ return count;
+ }
+ }
+
+ public IPNetwork this[BigInteger i]
+ {
+ get
+ {
+ if (i >= this.Count)
+ {
+ throw new ArgumentOutOfRangeException("i");
+ }
+
+ BigInteger last = this._ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetworkV6
+ ? this._lastUsable : this._broadcast;
+ BigInteger increment = (last - this._network) / this.Count;
+ BigInteger uintNetwork = this._network + ((increment + 1) * i);
+ IPNetwork ipn = new IPNetwork(uintNetwork, this._ipnetwork.AddressFamily, this._cidrSubnet);
+ return ipn;
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator<IPNetwork> IEnumerable<IPNetwork>.GetEnumerator()
+ {
+ return this;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this;
+ }
+
+ #region IEnumerator<IPNetwork> Members
+
+ public IPNetwork Current
+ {
+ get { return this[this._enumerator]; }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ // nothing to dispose
+ return;
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+
+ object IEnumerator.Current
+ {
+ get { return this.Current; }
+ }
+
+ public bool MoveNext()
+ {
+ this._enumerator++;
+ if (this._enumerator >= this.Count)
+ {
+ return false;
+ }
+ return true;
+
+ }
+
+ public void Reset()
+ {
+ this._enumerator = -1;
+ }
+
+ #endregion
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt b/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt
new file mode 100644
index 000000000..45d7392ac
--- /dev/null
+++ b/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt
@@ -0,0 +1,24 @@
+Copyright (c) 2015, lduchosal
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index 60da8a012..20abaf27c 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Server.Implementations/Networking/NetworkManager.cs
@@ -11,6 +11,8 @@ using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
+using System.Numerics;
namespace Emby.Server.Implementations.Networking
{
@@ -19,27 +21,32 @@ namespace Emby.Server.Implementations.Networking
protected ILogger Logger { get; private set; }
public event EventHandler NetworkChanged;
+ public Func<string[]> LocalSubnetsFn { get; set; }
- public NetworkManager(ILogger logger)
+ public NetworkManager(ILogger logger, IEnvironmentInfo environment)
{
Logger = logger;
- try
- {
- NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
- }
- catch (Exception ex)
+ // In FreeBSD these events cause a crash
+ if (environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.BSD)
{
- Logger.ErrorException("Error binding to NetworkAddressChanged event", ex);
- }
+ try
+ {
+ NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error binding to NetworkAddressChanged event", ex);
+ }
- try
- {
- NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex);
+ try
+ {
+ NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex);
+ }
}
}
@@ -60,6 +67,7 @@ namespace Emby.Server.Implementations.Networking
lock (_localIpAddressSyncLock)
{
_localIpAddresses = null;
+ _macAddresses = null;
}
if (NetworkChanged != null)
{
@@ -67,16 +75,16 @@ namespace Emby.Server.Implementations.Networking
}
}
- private List<IpAddressInfo> _localIpAddresses;
+ private IpAddressInfo[] _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
- public List<IpAddressInfo> GetLocalIpAddresses()
+ public IpAddressInfo[] GetLocalIpAddresses()
{
lock (_localIpAddressSyncLock)
{
if (_localIpAddresses == null)
{
- var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToList();
+ var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToArray();
_localIpAddresses = addresses;
@@ -120,6 +128,11 @@ namespace Emby.Server.Implementations.Networking
public bool IsInPrivateAddressSpace(string endpoint)
{
+ return IsInPrivateAddressSpace(endpoint, true);
+ }
+
+ private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets)
+ {
if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
{
return true;
@@ -146,12 +159,24 @@ namespace Emby.Server.Implementations.Networking
return Is172AddressPrivate(endpoint);
}
- return endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
+ if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
- endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) ||
- endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase) ||
- //endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
- IsInPrivateAddressSpaceAndLocalSubnet(endpoint);
+ endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint))
+ {
+ return true;
+ }
+
+ return false;
}
public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint)
@@ -238,9 +263,38 @@ namespace Emby.Server.Implementations.Networking
return IsInLocalNetworkInternal(endpoint, true);
}
- public bool IsInLocalNetworkInternal(string endpoint, bool resolveHost)
+ public bool IsAddressInSubnets(string addressString, string[] subnets)
{
- if (string.IsNullOrWhiteSpace(endpoint))
+ return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets);
+ }
+
+ private bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets)
+ {
+ foreach (var subnet in subnets)
+ {
+ var normalizedSubnet = subnet.Trim();
+
+ if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (normalizedSubnet.IndexOf('/') != -1)
+ {
+ var ipnetwork = IPNetwork.Parse(normalizedSubnet);
+ if (ipnetwork.Contains(address))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsInLocalNetworkInternal(string endpoint, bool resolveHost)
+ {
+ if (string.IsNullOrEmpty(endpoint))
{
throw new ArgumentNullException("endpoint");
}
@@ -250,11 +304,25 @@ namespace Emby.Server.Implementations.Networking
{
var addressString = address.ToString();
+ var localSubnetsFn = LocalSubnetsFn;
+ if (localSubnetsFn != null)
+ {
+ var localSubnets = localSubnetsFn();
+ foreach (var subnet in localSubnets)
+ {
+ // only validate if there's at least one valid entry
+ if (!string.IsNullOrWhiteSpace(subnet))
+ {
+ return IsAddressInSubnets(address, addressString, localSubnets) || IsInPrivateAddressSpace(addressString, false);
+ }
+ }
+ }
+
int lengthMatch = 100;
if (address.AddressFamily == AddressFamily.InterNetwork)
{
lengthMatch = 4;
- if (IsInPrivateAddressSpace(addressString))
+ if (IsInPrivateAddressSpace(addressString, true))
{
return true;
}
@@ -262,7 +330,7 @@ namespace Emby.Server.Implementations.Networking
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
lengthMatch = 9;
- if (IsInPrivateAddressSpace(endpoint))
+ if (IsInPrivateAddressSpace(endpoint, true))
{
return true;
}
@@ -353,13 +421,7 @@ namespace Emby.Server.Implementations.Networking
return new List<IPAddress>();
}
- //if (!_validNetworkInterfaceTypes.Contains(network.NetworkInterfaceType))
- //{
- // return new List<IPAddress>();
- //}
-
return ipProperties.UnicastAddresses
- //.Where(i => i.IsDnsEligible)
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6)
.ToList();
@@ -408,16 +470,40 @@ namespace Emby.Server.Implementations.Networking
}
}
- /// <summary>
- /// Returns MAC Address from first Network Card in Computer
- /// </summary>
- /// <returns>[string] MAC Address</returns>
- public string GetMacAddress()
+ private List<string> _macAddresses;
+ public List<string> GetMacAddresses()
+ {
+ if (_macAddresses == null)
+ {
+ _macAddresses = GetMacAddressesInternal();
+ }
+ return _macAddresses;
+ }
+
+ private List<string> GetMacAddressesInternal()
{
return NetworkInterface.GetAllNetworkInterfaces()
.Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
- .Select(i => BitConverter.ToString(i.GetPhysicalAddress().GetAddressBytes()))
- .FirstOrDefault();
+ .Select(i =>
+ {
+ try
+ {
+ var physicalAddress = i.GetPhysicalAddress();
+
+ if (physicalAddress == null)
+ {
+ return null;
+ }
+
+ return physicalAddress.ToString();
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+ })
+ .Where(i => i != null)
+ .ToList();
}
/// <summary>
diff --git a/Emby.Server.Implementations/News/NewsEntryPoint.cs b/Emby.Server.Implementations/News/NewsEntryPoint.cs
index 74366233c..3ce3d2315 100644
--- a/Emby.Server.Implementations/News/NewsEntryPoint.cs
+++ b/Emby.Server.Implementations/News/NewsEntryPoint.cs
@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.News
var requestOptions = new HttpRequestOptions
{
- Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
+ Url = "https://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
Progress = new SimpleProgress<double>(),
UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36",
BufferContent = false
@@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.News
Name = i.Title,
Description = i.Description,
Url = i.Link,
- UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList()
+ UserIds = _userManager.Users.Select(u => u.Id).ToArray()
}, cancellationToken));
@@ -274,7 +274,6 @@ namespace Emby.Server.Implementations.News
_timer.Dispose();
_timer = null;
}
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
deleted file mode 100644
index d74667c48..000000000
--- a/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Emby.Server.Implementations.Notifications
-{
- public interface IConfigurableNotificationService
- {
- bool IsHidden { get; }
- bool IsEnabled(string notificationType);
- }
-}
diff --git a/Emby.Server.Implementations/Notifications/InternalNotificationService.cs b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
deleted file mode 100644
index 61c564f18..000000000
--- a/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.Notifications;
-using System.Threading;
-using System.Threading.Tasks;
-using System;
-
-namespace Emby.Server.Implementations.Notifications
-{
- public class InternalNotificationService : INotificationService, IConfigurableNotificationService
- {
- private readonly INotificationsRepository _repo;
-
- public InternalNotificationService(INotificationsRepository repo)
- {
- _repo = repo;
- }
-
- public string Name
- {
- get { return "Dashboard Notifications"; }
- }
-
- public Task SendNotification(UserNotification request, CancellationToken cancellationToken)
- {
- return _repo.AddNotification(new Notification
- {
- Date = request.Date,
- Description = request.Description,
- Level = request.Level,
- Name = request.Name,
- Url = request.Url,
- UserId = request.User.Id.ToString("N")
-
- }, cancellationToken);
- }
-
- public bool IsEnabledForUser(User user)
- {
- return user.Policy.IsAdministrator;
- }
-
- public bool IsHidden
- {
- get { return true; }
- }
-
- public bool IsEnabled(string notificationType)
- {
- if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
- if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
- return true;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Notifications/Notifications.cs b/Emby.Server.Implementations/Notifications/Notifications.cs
deleted file mode 100644
index b7e1d6559..000000000
--- a/Emby.Server.Implementations/Notifications/Notifications.cs
+++ /dev/null
@@ -1,566 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Model.Threading;
-using MediaBrowser.Model.Dto;
-
-namespace Emby.Server.Implementations.Notifications
-{
- /// <summary>
- /// Creates notifications for various system events
- /// </summary>
- public class Notifications : IServerEntryPoint
- {
- private readonly IInstallationManager _installationManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
-
- private readonly ITaskManager _taskManager;
- private readonly INotificationManager _notificationManager;
-
- private readonly ILibraryManager _libraryManager;
- private readonly ISessionManager _sessionManager;
- private readonly IServerApplicationHost _appHost;
- private readonly ITimerFactory _timerFactory;
-
- private ITimer LibraryUpdateTimer { get; set; }
- private readonly object _libraryChangedSyncLock = new object();
-
- private readonly IConfigurationManager _config;
- private readonly IDeviceManager _deviceManager;
-
- public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory)
- {
- _installationManager = installationManager;
- _userManager = userManager;
- _logger = logger;
- _taskManager = taskManager;
- _notificationManager = notificationManager;
- _libraryManager = libraryManager;
- _sessionManager = sessionManager;
- _appHost = appHost;
- _config = config;
- _deviceManager = deviceManager;
- _timerFactory = timerFactory;
- }
-
- public void Run()
- {
- _installationManager.PluginInstalled += _installationManager_PluginInstalled;
- _installationManager.PluginUpdated += _installationManager_PluginUpdated;
- _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
- _installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
-
- _taskManager.TaskCompleted += _taskManager_TaskCompleted;
-
- _userManager.UserCreated += _userManager_UserCreated;
- _libraryManager.ItemAdded += _libraryManager_ItemAdded;
- _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
- _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
- _appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
- _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
-
- _userManager.UserLockedOut += _userManager_UserLockedOut;
- }
-
- async void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
- {
- var type = NotificationType.UserLockedOut.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["UserName"] = e.Argument.Name;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
- {
- var type = NotificationType.CameraImageUploaded.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["DeviceName"] = e.Argument.Device.Name;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- var type = NotificationType.ApplicationUpdateInstalled.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type,
- Url = e.Argument.infoUrl
- };
-
- notification.Variables["Version"] = e.Argument.versionStr;
- notification.Variables["ReleaseNotes"] = e.Argument.description;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
- {
- var type = NotificationType.PluginUpdateInstalled.ToString();
-
- var installationInfo = e.Argument.Item1;
-
- var notification = new NotificationRequest
- {
- Description = e.Argument.Item2.description,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.Name;
- notification.Variables["Version"] = installationInfo.Version.ToString();
- notification.Variables["ReleaseNotes"] = e.Argument.Item2.description;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- var type = NotificationType.PluginInstalled.ToString();
-
- var installationInfo = e.Argument;
-
- var notification = new NotificationRequest
- {
- Description = installationInfo.description,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.name;
- notification.Variables["Version"] = installationInfo.versionStr;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
- {
- // This notification is for users who can't auto-update (aka running as service)
- if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate)
- {
- return;
- }
-
- var type = NotificationType.ApplicationUpdateAvailable.ToString();
-
- var notification = new NotificationRequest
- {
- Description = "Please see emby.media for details.",
- NotificationType = type
- };
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
- {
- if (!_appHost.HasPendingRestart)
- {
- return;
- }
-
- var type = NotificationType.ServerRestartRequired.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- private NotificationOptions GetOptions()
- {
- return _config.GetConfiguration<NotificationOptions>("notifications");
- }
-
- void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.Warn("PlaybackStart reported with null media info.");
- return;
- }
-
- var video = e.Item as Video;
- if (video != null && video.IsThemeMedia)
- {
- return;
- }
-
- var type = GetPlaybackNotificationType(item.MediaType);
-
- SendPlaybackNotification(type, e);
- }
-
- void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.Warn("PlaybackStopped reported with null media info.");
- return;
- }
-
- var video = e.Item as Video;
- if (video != null && video.IsThemeMedia)
- {
- return;
- }
-
- var type = GetPlaybackStoppedNotificationType(item.MediaType);
-
- SendPlaybackNotification(type, e);
- }
-
- private async void SendPlaybackNotification(string type, PlaybackProgressEventArgs e)
- {
- var user = e.Users.FirstOrDefault();
-
- if (user != null && !GetOptions().IsEnabledToMonitorUser(type, user.Id.ToString("N")))
- {
- return;
- }
-
- var item = e.MediaInfo;
-
- if (e.Item != null && e.Item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- if (e.Item != null)
- {
- notification.Variables["ItemName"] = GetItemName(e.Item);
- }
- else
- {
- notification.Variables["ItemName"] = item.Name;
- }
-
- notification.Variables["UserName"] = user == null ? "Unknown user" : user.Name;
- notification.Variables["AppName"] = e.ClientName;
- notification.Variables["DeviceName"] = e.DeviceName;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- private string GetPlaybackNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlayback.ToString();
- }
- if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.GamePlayback.ToString();
- }
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlayback.ToString();
- }
-
- return null;
- }
-
- private string GetPlaybackStoppedNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlaybackStopped.ToString();
- }
- if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.GamePlaybackStopped.ToString();
- }
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlaybackStopped.ToString();
- }
-
- return null;
- }
-
- private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000,
- Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(5000, Timeout.Infinite);
- }
-
- _itemsAdded.Add(e.Item);
- }
- }
-
- private bool FilterItem(BaseItem item)
- {
- if (item.IsFolder)
- {
- return false;
- }
-
- if (item.LocationType == LocationType.Virtual)
- {
- return false;
- }
-
- if (item is IItemByName)
- {
- return false;
- }
-
- return item.SourceType == SourceType.Library;
- }
-
- private async void LibraryUpdateTimerCallback(object state)
- {
- List<BaseItem> items;
-
- lock (_libraryChangedSyncLock)
- {
- items = _itemsAdded.ToList();
- _itemsAdded.Clear();
- DisposeLibraryUpdateTimer();
- }
-
- items = items.Take(10).ToList();
-
- foreach (var item in items)
- {
- var notification = new NotificationRequest
- {
- NotificationType = NotificationType.NewLibraryContent.ToString()
- };
-
- notification.Variables["Name"] = GetItemName(item);
-
- await SendNotification(notification, item).ConfigureAwait(false);
- }
- }
-
- public static string GetItemName(BaseItem item)
- {
- var name = item.Name;
- var episode = item as Episode;
- if (episode != null)
- {
- if (episode.IndexNumber.HasValue)
- {
- name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
- }
- if (episode.ParentIndexNumber.HasValue)
- {
- name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
- }
- }
-
- var hasSeries = item as IHasSeries;
-
- if (hasSeries != null)
- {
- name = hasSeries.SeriesName + " - " + name;
- }
-
- var hasArtist = item as IHasArtist;
- if (hasArtist != null)
- {
- var artists = hasArtist.AllArtists;
-
- if (artists.Length > 0)
- {
- name = hasArtist.AllArtists[0] + " - " + name;
- }
- }
-
- return name;
- }
-
- public static string GetItemName(BaseItemDto item)
- {
- var name = item.Name;
-
- if (!string.IsNullOrWhiteSpace(item.SeriesName))
- {
- name = item.SeriesName + " - " + name;
- }
-
- if (item.Artists != null && item.Artists.Length > 0)
- {
- name = item.Artists[0] + " - " + name;
- }
-
- return name;
- }
-
- async void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
- {
- var notification = new NotificationRequest
- {
- UserIds = new List<string> { e.Argument.Id.ToString("N") },
- Name = "Welcome to Emby!",
- Description = "Check back here for more notifications."
- };
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- var result = e.Result;
-
- if (result.Status == TaskCompletionStatus.Failed)
- {
- var type = NotificationType.TaskFailed.ToString();
-
- var notification = new NotificationRequest
- {
- Description = result.ErrorMessage,
- Level = NotificationLevel.Error,
- NotificationType = type
- };
-
- notification.Variables["Name"] = result.Name;
- notification.Variables["ErrorMessage"] = result.ErrorMessage;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
- }
-
- async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
- {
- var type = NotificationType.PluginUninstalled.ToString();
-
- var plugin = e.Argument;
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["Name"] = plugin.Name;
- notification.Variables["Version"] = plugin.Version.ToString();
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- var installationInfo = e.InstallationInfo;
-
- var type = NotificationType.InstallationFailed.ToString();
-
- var notification = new NotificationRequest
- {
- Level = NotificationLevel.Error,
- Description = e.Exception.Message,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.Name;
- notification.Variables["Version"] = installationInfo.Version;
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
- private async Task SendNotification(NotificationRequest notification, BaseItem relatedItem)
- {
- try
- {
- await _notificationManager.SendNotification(notification, relatedItem, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending notification", ex);
- }
- }
-
- public void Dispose()
- {
- DisposeLibraryUpdateTimer();
-
- _installationManager.PluginInstalled -= _installationManager_PluginInstalled;
- _installationManager.PluginUpdated -= _installationManager_PluginUpdated;
- _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
- _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
-
- _taskManager.TaskCompleted -= _taskManager_TaskCompleted;
-
- _userManager.UserCreated -= _userManager_UserCreated;
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
-
- _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
- _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
-
- _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
- _userManager.UserLockedOut -= _userManager_UserLockedOut;
- GC.SuppressFinalize(this);
- }
-
- private void DisposeLibraryUpdateTimer()
- {
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
deleted file mode 100644
index f3a8a18ee..000000000
--- a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
+++ /dev/null
@@ -1,358 +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 Emby.Server.Implementations.Data;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Notifications;
-using SQLitePCL.pretty;
-using MediaBrowser.Model.Extensions;
-
-namespace Emby.Server.Implementations.Notifications
-{
- public class SqliteNotificationsRepository : BaseSqliteRepository, INotificationsRepository
- {
- protected IFileSystem FileSystem { get; private set; }
-
- public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) : base(logger)
- {
- FileSystem = fileSystem;
- DbFilePath = Path.Combine(appPaths.DataPath, "notifications.db");
- }
-
- public event EventHandler<NotificationUpdateEventArgs> NotificationAdded;
- public event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
- ////public event EventHandler<NotificationUpdateEventArgs> NotificationUpdated;
-
- public void Initialize()
- {
- try
- {
- InitializeInternal();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
-
- FileSystem.DeleteFile(DbFilePath);
-
- InitializeInternal();
- }
- }
-
- private void InitializeInternal()
- {
- using (var connection = CreateConnection())
- {
- RunDefaultInitialization(connection);
-
- string[] queries = {
-
- "create table if not exists Notifications (Id GUID NOT NULL, UserId GUID NOT NULL, Date DATETIME NOT NULL, Name TEXT NOT NULL, Description TEXT NULL, Url TEXT NULL, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT NULL, PRIMARY KEY (Id, UserId))",
- "create index if not exists idx_Notifications1 on Notifications(Id)",
- "create index if not exists idx_Notifications2 on Notifications(UserId)"
- };
-
- connection.RunQueries(queries);
- }
- }
-
- /// <summary>
- /// Gets the notifications.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>NotificationResult.</returns>
- public NotificationResult GetNotifications(NotificationQuery query)
- {
- var result = new NotificationResult();
-
- var clauses = new List<string>();
- var paramList = new List<object>();
-
- if (query.IsRead.HasValue)
- {
- clauses.Add("IsRead=?");
- paramList.Add(query.IsRead.Value);
- }
-
- clauses.Add("UserId=?");
- paramList.Add(query.UserId.ToGuidBlob());
-
- 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(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);
-
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
-
- if (query.Limit.HasValue || offset > 0)
- {
- commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
-
- if (offset > 0)
- {
- commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
- }
-
- var resultList = new List<Notification>();
-
- foreach (var row in connection.Query(commandText, paramList.ToArray(paramList.Count)))
- {
- resultList.Add(GetNotification(row));
- }
-
- result.Notifications = resultList.ToArray(resultList.Count);
- }
- }
-
- return result;
- }
-
- public NotificationsSummary GetNotificationsSummary(string userId)
- {
- var result = new NotificationsSummary();
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- using (var statement = connection.PrepareStatement("select Level from Notifications where UserId=@UserId and IsRead=@IsRead"))
- {
- statement.TryBind("@IsRead", false);
- statement.TryBind("@UserId", userId.ToGuidBlob());
-
- var levels = new List<NotificationLevel>();
-
- foreach (var row in statement.ExecuteQuery())
- {
- levels.Add(GetLevel(row, 0));
- }
-
- result.UnreadCount = levels.Count;
-
- if (levels.Count > 0)
- {
- result.MaxUnreadNotificationLevel = levels.Max();
- }
- }
-
- return result;
- }
- }
- }
-
- private Notification GetNotification(IReadOnlyList<IResultSetValue> reader)
- {
- var notification = new Notification
- {
- Id = reader[0].ReadGuidFromBlob().ToString("N"),
- UserId = reader[1].ReadGuidFromBlob().ToString("N"),
- Date = reader[2].ReadDateTime(),
- Name = reader[3].ToString()
- };
-
- if (reader[4].SQLiteType != SQLiteType.Null)
- {
- notification.Description = reader[4].ToString();
- }
-
- if (reader[5].SQLiteType != SQLiteType.Null)
- {
- notification.Url = reader[5].ToString();
- }
-
- notification.Level = GetLevel(reader, 6);
- notification.IsRead = reader[7].ToBool();
-
- return notification;
- }
-
- /// <summary>
- /// Gets the level.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="index">The index.</param>
- /// <returns>NotificationLevel.</returns>
- private NotificationLevel GetLevel(IReadOnlyList<IResultSetValue> reader, int index)
- {
- NotificationLevel level;
-
- var val = reader[index].ToString();
-
- Enum.TryParse(val, true, out level);
-
- return level;
- }
-
- /// <summary>
- /// Adds the notification.
- /// </summary>
- /// <param name="notification">The notification.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task AddNotification(Notification notification, CancellationToken cancellationToken)
- {
- await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false);
-
- if (NotificationAdded != null)
- {
- try
- {
- NotificationAdded(this, new NotificationUpdateEventArgs
- {
- Notification = notification
- });
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in NotificationAdded event handler", ex);
- }
- }
- }
-
- /// <summary>
- /// Replaces the notification.
- /// </summary>
- /// <param name="notification">The notification.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task ReplaceNotification(Notification notification, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(notification.Id))
- {
- notification.Id = Guid.NewGuid().ToString("N");
- }
- if (string.IsNullOrEmpty(notification.UserId))
- {
- throw new ArgumentException("The notification must have a user id");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- lock (WriteLock)
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(conn =>
- {
- using (var statement = conn.PrepareStatement("replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"))
- {
- statement.TryBind("@Id", notification.Id.ToGuidBlob());
- statement.TryBind("@UserId", notification.UserId.ToGuidBlob());
- statement.TryBind("@Date", notification.Date.ToDateTimeParamValue());
- statement.TryBind("@Name", notification.Name);
- statement.TryBind("@Description", notification.Description);
- statement.TryBind("@Url", notification.Url);
- statement.TryBind("@Level", notification.Level.ToString());
- statement.TryBind("@IsRead", notification.IsRead);
- statement.TryBind("@Category", string.Empty);
- statement.TryBind("@RelatedId", string.Empty);
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- /// <summary>
- /// Marks the read.
- /// </summary>
- /// <param name="notificationIdList">The notification id list.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="isRead">if set to <c>true</c> [is read].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- 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(list.Count);
-
- await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false);
-
- if (NotificationsMarkedRead != null)
- {
- try
- {
- NotificationsMarkedRead(this, new NotificationReadEventArgs
- {
- IdList = list.ToArray(list.Count),
- IsRead = isRead,
- UserId = userId
- });
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in NotificationsMarkedRead event handler", ex);
- }
- }
- }
-
- public async Task MarkAllRead(string userId, bool isRead, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(conn =>
- {
- using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId"))
- {
- statement.TryBind("@IsRead", isRead);
- statement.TryBind("@UserId", userId.ToGuidBlob());
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- private async Task MarkReadInternal(IEnumerable<Guid> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(conn =>
- {
- using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId and Id=@Id"))
- {
- statement.TryBind("@IsRead", isRead);
- statement.TryBind("@UserId", userId.ToGuidBlob());
-
- foreach (var id in notificationIdList)
- {
- statement.Reset();
-
- statement.TryBind("@Id", id.ToGuidBlob());
-
- statement.MoveNext();
- }
- }
-
- }, TransactionMode);
- }
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
deleted file mode 100644
index 6e57e7f81..000000000
--- a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using System.Linq;
-using MediaBrowser.Model.Extensions;
-
-namespace Emby.Server.Implementations.Notifications
-{
- /// <summary>
- /// Notifies clients anytime a notification is added or udpated
- /// </summary>
- public class WebSocketNotifier : IServerEntryPoint
- {
- private readonly INotificationsRepository _notificationsRepo;
-
- private readonly IServerManager _serverManager;
-
- public WebSocketNotifier(INotificationsRepository notificationsRepo, IServerManager serverManager)
- {
- _notificationsRepo = notificationsRepo;
- _serverManager = serverManager;
- }
-
- public void Run()
- {
- _notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded;
- _notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead;
- }
-
- void _notificationsRepo_NotificationsMarkedRead(object sender, NotificationReadEventArgs e)
- {
- var list = e.IdList.ToList();
-
- list.Add(e.UserId);
- list.Add(e.IsRead.ToString().ToLower());
-
- var msg = string.Join("|", list.ToArray(list.Count));
-
- _serverManager.SendWebSocketMessage("NotificationsMarkedRead", msg);
- }
-
- void _notificationsRepo_NotificationAdded(object sender, NotificationUpdateEventArgs e)
- {
- var msg = e.Notification.UserId + "|" + e.Notification.Id;
-
- _serverManager.SendWebSocketMessage("NotificationAdded", msg);
- }
-
- public void Dispose()
- {
- _notificationsRepo.NotificationAdded -= _notificationsRepo_NotificationAdded;
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index 2a178895c..908fa65de 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -50,8 +50,16 @@ namespace Emby.Server.Implementations.Playlists
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
{
- query.Recursive = false;
- return base.GetItemsInternal(query);
+ if (query.User == null)
+ {
+ query.Recursive = false;
+ return base.GetItemsInternal(query);
+ }
+
+ query.Recursive = true;
+ query.IncludeItemTypes = new string[] { "Playlist" };
+ query.Parent = null;
+ return LibraryManager.GetItemsResult(query);
}
}
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 36e8b4dd8..e69b4a34e 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -28,11 +28,11 @@ namespace Emby.Server.Implementations.Playlists
{
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
var playlist = (Playlist)item;
- var items = playlist.GetManageableItems()
+ return playlist.GetManageableItems()
.Select(i =>
{
var subItem = i.Item2;
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Playlists
return subItem;
}
- var parent = subItem.IsOwnedItem ? subItem.GetOwner() : subItem.GetParent();
+ var parent = subItem.GetOwner() ?? subItem.GetParent();
if (parent != null && parent.HasImage(ImageType.Primary))
{
@@ -66,9 +66,9 @@ namespace Emby.Server.Implementations.Playlists
return null;
})
.Where(i => i != null)
- .DistinctBy(i => i.Id);
-
- return GetFinalItems(items);
+ .OrderBy(i => Guid.NewGuid())
+ .DistinctBy(i => i.Id)
+ .ToList();
}
}
@@ -81,27 +81,20 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
- var items = _libraryManager.GetItemList(new InternalItemsQuery
+ return _libraryManager.GetItemList(new InternalItemsQuery
{
Genres = new[] { item.Name },
IncludeItemTypes = new[] { typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Audio).Name },
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4,
Recursive = true,
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
});
-
- return GetFinalItems(items);
}
-
- //protected override Task<string> CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- //{
- // return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
- //}
}
public class GenreImageProvider : BaseDynamicImageProvider<Genre>
@@ -113,21 +106,18 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
- var items = _libraryManager.GetItemList(new InternalItemsQuery
+ return _libraryManager.GetItemList(new InternalItemsQuery
{
Genres = new[] { item.Name },
IncludeItemTypes = new[] { typeof(Series).Name, typeof(Movie).Name },
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4,
Recursive = true,
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
-
});
-
- return GetFinalItems(items);
}
//protected override Task<string> CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index f268e9c0c..e0a6f56ec 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -15,6 +15,10 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using PlaylistsNET;
+using PlaylistsNET.Content;
+using PlaylistsNET.Models;
+using PlaylistsNET.Utils;
namespace Emby.Server.Implementations.Playlists
{
@@ -37,7 +41,7 @@ namespace Emby.Server.Implementations.Playlists
_providerManager = providerManager;
}
- public IEnumerable<Playlist> GetPlaylists(string userId)
+ public IEnumerable<Playlist> GetPlaylists(Guid userId)
{
var user = _userManager.GetUserById(userId);
@@ -50,14 +54,14 @@ namespace Emby.Server.Implementations.Playlists
var folderName = _fileSystem.GetValidFilename(name) + " [playlist]";
- var parentFolder = GetPlaylistsFolder(null);
+ var parentFolder = GetPlaylistsFolder(Guid.Empty);
if (parentFolder == null)
{
throw new ArgumentException();
}
- if (string.IsNullOrWhiteSpace(options.MediaType))
+ if (string.IsNullOrEmpty(options.MediaType))
{
foreach (var itemId in options.ItemIdList)
{
@@ -68,7 +72,7 @@ namespace Emby.Server.Implementations.Playlists
throw new ArgumentException("No item exists with the supplied Id");
}
- if (!string.IsNullOrWhiteSpace(item.MediaType))
+ if (!string.IsNullOrEmpty(item.MediaType))
{
options.MediaType = item.MediaType;
}
@@ -87,18 +91,18 @@ namespace Emby.Server.Implementations.Playlists
{
options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
.Select(i => i.MediaType)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ .FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
}
- if (!string.IsNullOrWhiteSpace(options.MediaType))
+ if (!string.IsNullOrEmpty(options.MediaType))
{
break;
}
}
}
- if (string.IsNullOrWhiteSpace(options.MediaType))
+ if (string.IsNullOrEmpty(options.MediaType))
{
options.MediaType = "Audio";
}
@@ -117,15 +121,17 @@ namespace Emby.Server.Implementations.Playlists
var playlist = new Playlist
{
Name = name,
- Path = path
+ Path = path,
+ Shares = new[]
+ {
+ new Share
+ {
+ UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N"),
+ CanEdit = true
+ }
+ }
};
- playlist.Shares.Add(new Share
- {
- UserId = options.UserId,
- CanEdit = true
- });
-
playlist.SetMediaType(options.MediaType);
parentFolder.AddChild(playlist, CancellationToken.None);
@@ -135,7 +141,7 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0)
{
- await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
+ AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
{
EnableImages = true
});
@@ -163,24 +169,24 @@ namespace Emby.Server.Implementations.Playlists
return path;
}
- private List<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user, DtoOptions options)
+ private List<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, string playlistMediaType, User user, DtoOptions options)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
- public Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId)
+ public void AddToPlaylist(string playlistId, IEnumerable<Guid> itemIds, Guid userId)
{
- var user = string.IsNullOrWhiteSpace(userId) ? null : _userManager.GetUserById(userId);
+ var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
- return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
+ AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
EnableImages = true
});
}
- private async Task AddToPlaylistInternal(string playlistId, IEnumerable<string> itemIds, User user, DtoOptions options)
+ private void AddToPlaylistInternal(string playlistId, IEnumerable<Guid> itemIds, User user, DtoOptions options)
{
var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
@@ -197,11 +203,6 @@ namespace Emby.Server.Implementations.Playlists
foreach (var item in items)
{
- if (string.IsNullOrWhiteSpace(item.Path))
- {
- continue;
- }
-
list.Add(LinkedChild.Create(item));
}
@@ -211,6 +212,11 @@ namespace Emby.Server.Implementations.Playlists
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ if (playlist.IsFile)
+ {
+ SavePlaylistFile(playlist);
+ }
+
_providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem)
{
ForceSave = true
@@ -218,7 +224,7 @@ namespace Emby.Server.Implementations.Playlists
}, RefreshPriority.High);
}
- public async Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
+ public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
{
var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
@@ -239,6 +245,11 @@ namespace Emby.Server.Implementations.Playlists
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ if (playlist.IsFile)
+ {
+ SavePlaylistFile(playlist);
+ }
+
_providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem)
{
ForceSave = true
@@ -246,7 +257,7 @@ namespace Emby.Server.Implementations.Playlists
}, RefreshPriority.High);
}
- public async Task MoveItem(string playlistId, string entryId, int newIndex)
+ public void MoveItem(string playlistId, string entryId, int newIndex)
{
var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
@@ -282,9 +293,207 @@ namespace Emby.Server.Implementations.Playlists
playlist.LinkedChildren = newList.ToArray(newList.Count);
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+
+ if (playlist.IsFile)
+ {
+ SavePlaylistFile(playlist);
+ }
+ }
+
+ private void SavePlaylistFile(Playlist item)
+ {
+ // This is probably best done as a metatata provider, but saving a file over itself will first require some core work to prevent this from happening when not needed
+ var playlistPath = item.Path;
+ var extension = Path.GetExtension(playlistPath);
+
+ if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ var playlist = new WplPlaylist();
+ foreach (var child in item.GetLinkedChildren())
+ {
+ var entry = new WplPlaylistEntry()
+ {
+ Path = NormalizeItemPath(playlistPath, child.Path),
+ TrackTitle = child.Name,
+ AlbumTitle = child.Album
+ };
+
+ var hasAlbumArtist = child as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ var hasArtist = child as IHasArtist;
+ if (hasArtist != null)
+ {
+ entry.TrackArtist = hasArtist.Artists.FirstOrDefault();
+ }
+
+ if (child.RunTimeTicks.HasValue)
+ {
+ entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
+ }
+ playlist.PlaylistEntries.Add(entry);
+ }
+
+ _fileSystem.WriteAllText(playlistPath, new WplContent().ToText(playlist));
+ }
+ if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ var playlist = new ZplPlaylist();
+ foreach (var child in item.GetLinkedChildren())
+ {
+ var entry = new ZplPlaylistEntry()
+ {
+ Path = NormalizeItemPath(playlistPath, child.Path),
+ TrackTitle = child.Name,
+ AlbumTitle = child.Album
+ };
+
+ var hasAlbumArtist = child as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ var hasArtist = child as IHasArtist;
+ if (hasArtist != null)
+ {
+ entry.TrackArtist = hasArtist.Artists.FirstOrDefault();
+ }
+
+ if (child.RunTimeTicks.HasValue)
+ {
+ entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
+ }
+ playlist.PlaylistEntries.Add(entry);
+ }
+
+ _fileSystem.WriteAllText(playlistPath, new ZplContent().ToText(playlist));
+ }
+ if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ var playlist = new M3uPlaylist();
+ playlist.IsExtended = true;
+ foreach (var child in item.GetLinkedChildren())
+ {
+ var entry = new M3uPlaylistEntry()
+ {
+ Path = NormalizeItemPath(playlistPath, child.Path),
+ Title = child.Name,
+ Album = child.Album
+ };
+
+ var hasAlbumArtist = child as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ if (child.RunTimeTicks.HasValue)
+ {
+ entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
+ }
+ playlist.PlaylistEntries.Add(entry);
+ }
+
+ _fileSystem.WriteAllText(playlistPath, new M3uContent().ToText(playlist));
+ }
+ if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ var playlist = new M3uPlaylist();
+ playlist.IsExtended = true;
+ foreach (var child in item.GetLinkedChildren())
+ {
+ var entry = new M3uPlaylistEntry()
+ {
+ Path = NormalizeItemPath(playlistPath, child.Path),
+ Title = child.Name,
+ Album = child.Album
+ };
+
+ var hasAlbumArtist = child as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ if (child.RunTimeTicks.HasValue)
+ {
+ entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
+ }
+ playlist.PlaylistEntries.Add(entry);
+ }
+
+ _fileSystem.WriteAllText(playlistPath, new M3u8Content().ToText(playlist));
+ }
+ if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ var playlist = new PlsPlaylist();
+ foreach (var child in item.GetLinkedChildren())
+ {
+ var entry = new PlsPlaylistEntry()
+ {
+ Path = NormalizeItemPath(playlistPath, child.Path),
+ Title = child.Name
+ };
+
+ if (child.RunTimeTicks.HasValue)
+ {
+ entry.Length = TimeSpan.FromTicks(child.RunTimeTicks.Value);
+ }
+ playlist.PlaylistEntries.Add(entry);
+ }
+
+ _fileSystem.WriteAllText(playlistPath, new PlsContent().ToText(playlist));
+ }
+ }
+
+ private string NormalizeItemPath(string playlistPath, string itemPath)
+ {
+ return MakeRelativePath(_fileSystem.GetDirectoryName(playlistPath), itemPath);
+ }
+
+ private static String MakeRelativePath(string folderPath, string fileAbsolutePath)
+ {
+ if (String.IsNullOrEmpty(folderPath)) throw new ArgumentNullException("folderPath");
+ if (String.IsNullOrEmpty(fileAbsolutePath)) throw new ArgumentNullException("filePath");
+
+ if (!folderPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
+ {
+ folderPath = folderPath + Path.DirectorySeparatorChar;
+ }
+
+ Uri folderUri = new Uri(folderPath);
+ Uri fileAbsoluteUri = new Uri(fileAbsolutePath);
+
+ if (folderUri.Scheme != fileAbsoluteUri.Scheme) { return fileAbsolutePath; } // path can't be made relative.
+
+ Uri relativeUri = folderUri.MakeRelativeUri(fileAbsoluteUri);
+ String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
+
+ if (fileAbsoluteUri.Scheme.Equals("file", StringComparison.CurrentCultureIgnoreCase))
+ {
+ relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ }
+
+ return relativePath;
+ }
+
+ private static string UnEscape(string content)
+ {
+ if (content == null) return content;
+ return content.Replace("&amp;", "&").Replace("&apos;", "'").Replace("&quot;", "\"").Replace("&gt;", ">").Replace("&lt;", "<");
+ }
+
+ private static string Escape(string content)
+ {
+ if (content == null) return null;
+ return content.Replace("&", "&amp;").Replace("'", "&apos;").Replace("\"", "&quot;").Replace(">", "&gt;").Replace("<", "&lt;");
}
- public Folder GetPlaylistsFolder(string userId)
+ public Folder GetPlaylistsFolder(Guid userId)
{
var typeName = "PlaylistsFolder";
diff --git a/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs b/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs
deleted file mode 100644
index 2ce835576..000000000
--- a/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.IO;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.IO;
-
-namespace Emby.Server.Implementations.Playlists
-{
- public class PlaylistsDynamicFolder : IVirtualFolderCreator
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
-
- public PlaylistsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem)
- {
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- }
-
- public BasePluginFolder GetFolder()
- {
- var path = Path.Combine(_appPaths.DataPath, "playlists");
-
- _fileSystem.CreateDirectory(path);
-
- return new PlaylistsFolder
- {
- Path = path
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/ResourceFileManager.cs b/Emby.Server.Implementations/ResourceFileManager.cs
new file mode 100644
index 000000000..ce29b52f6
--- /dev/null
+++ b/Emby.Server.Implementations/ResourceFileManager.cs
@@ -0,0 +1,74 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Reflection;
+using MediaBrowser.Model.Services;
+
+namespace Emby.Server.Implementations
+{
+ public class ResourceFileManager : IResourceFileManager
+ {
+ private readonly IFileSystem _fileSystem;
+ private readonly ILogger _logger;
+ private readonly IHttpResultFactory _resultFactory;
+
+ public ResourceFileManager(IHttpResultFactory resultFactory, ILogger logger, IFileSystem fileSystem)
+ {
+ _resultFactory = resultFactory;
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
+ public Stream GetResourceFileStream(string basePath, string virtualPath)
+ {
+ return _fileSystem.GetFileStream(GetResourcePath(basePath, virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+ }
+
+ public Task<object> GetStaticFileResult(IRequest request, string basePath, string virtualPath, string contentType, TimeSpan? cacheDuration)
+ {
+ return _resultFactory.GetStaticFileResult(request, GetResourcePath(basePath, virtualPath));
+ }
+
+ public string ReadAllText(string basePath, string virtualPath)
+ {
+ return _fileSystem.ReadAllText(GetResourcePath(basePath, virtualPath));
+ }
+
+ private string GetResourcePath(string basePath, string virtualPath)
+ {
+ var fullPath = Path.Combine(basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
+
+ try
+ {
+ fullPath = _fileSystem.GetFullPath(fullPath);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in Path.GetFullPath", ex);
+ }
+
+ // Don't allow file system access outside of the source folder
+ if (!_fileSystem.ContainsSubPath(basePath, fullPath))
+ {
+ throw new SecurityException("Access denied");
+ }
+
+ return fullPath;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
index fe0652f66..7a5efded3 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
Type = TaskTriggerInfo.TriggerDaily,
TimeOfDayTicks = TimeSpan.FromHours(2).Ticks,
- MaxRuntimeMs = Convert.ToInt32(TimeSpan.FromHours(4).TotalMilliseconds)
+ MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks
}
};
}
@@ -133,9 +133,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
try
{
- var chapters = _itemRepo.GetChapters(video.Id);
+ var chapters = _itemRepo.GetChapters(video);
- var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, CancellationToken.None);
+ var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
if (!success)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs
index 1ba5d4329..a2779c7cd 100644
--- a/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs
@@ -19,18 +19,15 @@ namespace Emby.Server.Implementations.ScheduledTasks
public TimeSpan TimeOfDay { get; set; }
/// <summary>
- /// Gets or sets the timer.
+ /// Gets or sets the options of this task.
/// </summary>
- /// <value>The timer.</value>
- private Timer Timer { get; set; }
+ public TaskOptions TaskOptions { get; set; }
/// <summary>
- /// Gets the execution properties of this task.
+ /// Gets or sets the timer.
/// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- public TaskExecutionOptions TaskOptions { get; set; }
+ /// <value>The timer.</value>
+ private Timer Timer { get; set; }
/// <summary>
/// Stars waiting for the trigger action
@@ -75,7 +72,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Occurs when [triggered].
/// </summary>
- public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
+ public event EventHandler<EventArgs> Triggered;
/// <summary>
/// Called when [triggered].
@@ -84,7 +81,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (Triggered != null)
{
- Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
+ Triggered(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs
index d09765e34..dcccb252a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs
@@ -19,18 +19,15 @@ namespace Emby.Server.Implementations.ScheduledTasks
public TimeSpan Interval { get; set; }
/// <summary>
- /// Gets or sets the timer.
+ /// Gets or sets the options of this task.
/// </summary>
- /// <value>The timer.</value>
- private Timer Timer { get; set; }
+ public TaskOptions TaskOptions { get; set; }
/// <summary>
- /// Gets the execution properties of this task.
+ /// Gets or sets the timer.
/// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- public TaskExecutionOptions TaskOptions { get; set; }
+ /// <value>The timer.</value>
+ private Timer Timer { get; set; }
private DateTime _lastStartDate;
@@ -93,7 +90,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Occurs when [triggered].
/// </summary>
- public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
+ public event EventHandler<EventArgs> Triggered;
/// <summary>
/// Called when [triggered].
@@ -105,7 +102,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
if (Triggered != null)
{
_lastStartDate = DateTime.UtcNow;
- Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
+ Triggered(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
index 9f887ba03..691112638 100644
--- a/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Plugin Update Task
/// </summary>
- public class PluginUpdateTask : IScheduledTask
+ public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask
{
/// <summary>
/// The _logger
@@ -71,14 +71,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
var numComplete = 0;
- // Create tasks for each one
- var tasks = packagesToInstall.Select(i => Task.Run(async () =>
+ foreach (var package in packagesToInstall)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
- await _installationManager.InstallPackage(i, true, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+ await _installationManager.InstallPackage(package, true, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -90,11 +89,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (HttpException ex)
{
- _logger.ErrorException("Error downloading {0}", ex, i.name);
+ _logger.ErrorException("Error downloading {0}", ex, package.name);
}
catch (IOException ex)
{
- _logger.ErrorException("Error updating {0}", ex, i.name);
+ _logger.ErrorException("Error updating {0}", ex, package.name);
}
// Update progress
@@ -106,11 +105,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
progress.Report(90 * percent + 10);
}
- }));
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
+ }
progress.Report(100);
}
@@ -137,5 +132,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
get { return "Application"; }
}
+
+ public bool IsHidden => true;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index bdc29c16b..f6397b670 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -160,7 +160,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
_lastExecutionResult = value;
var path = GetHistoryFilePath();
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
lock (_lastExecutionResultSyncLock)
{
@@ -236,7 +236,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// The _triggers
/// </summary>
- private Tuple<TaskTriggerInfo,ITaskTrigger>[] _triggers;
+ private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
/// <summary>
/// Gets the triggers that define when the task will run
/// </summary>
@@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- async void trigger_Triggered(object sender, GenericEventArgs<TaskExecutionOptions> e)
+ async void trigger_Triggered(object sender, EventArgs e)
{
var trigger = (ITaskTrigger)sender;
@@ -365,7 +365,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
trigger.Stop();
- TaskManager.QueueScheduledTask(ScheduledTask, e.Argument);
+ TaskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions);
await Task.Delay(1000).ConfigureAwait(false);
@@ -380,7 +380,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="options">Task options.</param>
/// <returns>Task.</returns>
/// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception>
- public async Task Execute(TaskExecutionOptions options)
+ public async Task Execute(TaskOptions options)
{
var task = Task.Run(async () => await ExecuteInternal(options).ConfigureAwait(false));
@@ -397,7 +397,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- private async Task ExecuteInternal(TaskExecutionOptions options)
+ private async Task ExecuteInternal(TaskOptions options)
{
// Cancel the current execution, if any
if (CurrentCancellationTokenSource != null)
@@ -422,14 +422,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
try
{
- if (options != null && options.MaxRuntimeMs.HasValue)
+ if (options != null && options.MaxRuntimeTicks.HasValue)
{
- CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
+ CurrentCancellationTokenSource.CancelAfter(TimeSpan.FromTicks(options.MaxRuntimeTicks.Value));
}
- var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
-
- await localTask.ConfigureAwait(false);
+ await ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress).ConfigureAwait(false);
status = TaskCompletionStatus.Completed;
}
@@ -563,13 +561,35 @@ namespace Emby.Server.Implementations.ScheduledTasks
catch (FileNotFoundException)
{
// File doesn't exist. No biggie. Return defaults.
- return ScheduledTask.GetDefaultTriggers().ToArray();
}
catch (DirectoryNotFoundException)
{
// File doesn't exist. No biggie. Return defaults.
}
- return ScheduledTask.GetDefaultTriggers().ToArray();
+ catch
+ {
+
+ }
+ return GetDefaultTriggers();
+ }
+
+ private TaskTriggerInfo[] GetDefaultTriggers()
+ {
+ try
+ {
+ return ScheduledTask.GetDefaultTriggers().ToArray();
+ }
+ catch
+ {
+ return new TaskTriggerInfo[]
+ {
+ new TaskTriggerInfo
+ {
+ IntervalTicks = TimeSpan.FromDays(1).Ticks,
+ Type = TaskTriggerInfo.TriggerInterval
+ }
+ };
+ }
}
/// <summary>
@@ -580,7 +600,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var path = GetConfigurationFilePath();
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
JsonSerializer.SerializeToFile(triggers, path);
}
@@ -625,7 +645,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -705,9 +724,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception>
private ITaskTrigger GetTrigger(TaskTriggerInfo info)
{
- var options = new TaskExecutionOptions
+ var options = new TaskOptions
{
- MaxRuntimeMs = info.MaxRuntimeMs
+ MaxRuntimeTicks = info.MaxRuntimeTicks
};
if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase))
diff --git a/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs
index d708c905d..20a4304cc 100644
--- a/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs
@@ -14,12 +14,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
public int DelayMs { get; set; }
/// <summary>
- /// Gets the execution properties of this task.
+ /// Gets or sets the options of this task.
/// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- public TaskExecutionOptions TaskOptions { get; set; }
+ public TaskOptions TaskOptions { get; set; }
public StartupTrigger()
{
@@ -51,7 +48,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Occurs when [triggered].
/// </summary>
- public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
+ public event EventHandler<EventArgs> Triggered;
/// <summary>
/// Called when [triggered].
@@ -60,7 +57,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (Triggered != null)
{
- Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
+ Triggered(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/SystemEventTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/SystemEventTrigger.cs
index 976754a40..c4623bf5b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/SystemEventTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/SystemEventTrigger.cs
@@ -19,12 +19,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
public SystemEvent SystemEvent { get; set; }
/// <summary>
- /// Gets the execution properties of this task.
+ /// Gets or sets the options of this task.
/// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- public TaskExecutionOptions TaskOptions { get; set; }
+ public TaskOptions TaskOptions { get; set; }
private readonly ISystemEvents _systemEvents;
@@ -70,7 +67,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Occurs when [triggered].
/// </summary>
- public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
+ public event EventHandler<EventArgs> Triggered;
/// <summary>
/// Called when [triggered].
@@ -79,7 +76,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (Triggered != null)
{
- Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
+ Triggered(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index c0fcb459d..8963693ab 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -32,8 +32,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// The _task queue
/// </summary>
- private readonly ConcurrentQueue<Tuple<Type, TaskExecutionOptions>> _taskQueue =
- new ConcurrentQueue<Tuple<Type, TaskExecutionOptions>>();
+ private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
+ new ConcurrentQueue<Tuple<Type, TaskOptions>>();
/// <summary>
/// Gets or sets the json serializer.
@@ -114,6 +114,10 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var path = Path.Combine(ApplicationPaths.CachePath, "startuptasks.txt");
+ // ToDo: Fix this shit
+ if (!File.Exists(path))
+ return;
+
List<string> lines;
try
@@ -126,7 +130,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
if (task != null)
{
- QueueScheduledTask(task, new TaskExecutionOptions());
+ QueueScheduledTask(task, new TaskOptions());
}
}
@@ -143,7 +147,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="options">Task options.</param>
- public void CancelIfRunningAndQueue<T>(TaskExecutionOptions options)
+ public void CancelIfRunningAndQueue<T>(TaskOptions options)
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
@@ -155,7 +159,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void CancelIfRunningAndQueue<T>()
where T : IScheduledTask
{
- CancelIfRunningAndQueue<T>(new TaskExecutionOptions());
+ CancelIfRunningAndQueue<T>(new TaskOptions());
}
/// <summary>
@@ -174,7 +178,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="options">Task options</param>
- public void QueueScheduledTask<T>(TaskExecutionOptions options)
+ public void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
@@ -192,7 +196,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void QueueScheduledTask<T>()
where T : IScheduledTask
{
- QueueScheduledTask<T>(new TaskExecutionOptions());
+ QueueScheduledTask<T>(new TaskOptions());
}
public void QueueIfNotRunning<T>()
@@ -202,7 +206,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
if (task.State != TaskState.Running)
{
- QueueScheduledTask<T>(new TaskExecutionOptions());
+ QueueScheduledTask<T>(new TaskOptions());
}
}
@@ -225,7 +229,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (scheduledTask.State == TaskState.Idle)
{
- Execute(scheduledTask, new TaskExecutionOptions());
+ Execute(scheduledTask, new TaskOptions());
}
}
}
@@ -236,7 +240,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="task">The task.</param>
/// <param name="options">The task options.</param>
- public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options)
+ public void QueueScheduledTask(IScheduledTask task, TaskOptions options)
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
@@ -255,7 +259,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="task">The task.</param>
/// <param name="options">The task options.</param>
- private void QueueScheduledTask(IScheduledTaskWorker task, TaskExecutionOptions options)
+ private void QueueScheduledTask(IScheduledTaskWorker task, TaskOptions options)
{
var type = task.ScheduledTask.GetType();
@@ -269,7 +273,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
return;
}
- _taskQueue.Enqueue(new Tuple<Type, TaskExecutionOptions>(type, options));
+ _taskQueue.Enqueue(new Tuple<Type, TaskOptions>(type, options));
}
}
@@ -297,7 +301,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
@@ -317,7 +320,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
((ScheduledTaskWorker)task).Cancel();
}
- public Task Execute(IScheduledTaskWorker task, TaskExecutionOptions options)
+ public Task Execute(IScheduledTaskWorker task, TaskOptions options)
{
return ((ScheduledTaskWorker)task).Execute(options);
}
@@ -362,9 +365,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
// Execute queued tasks
lock (_taskQueue)
{
- var list = new List<Tuple<Type, TaskExecutionOptions>>();
+ var list = new List<Tuple<Type, TaskOptions>>();
- Tuple<Type, TaskExecutionOptions> item;
+ Tuple<Type, TaskOptions> item;
while (_taskQueue.TryDequeue(out item))
{
if (list.All(i => i.Item1 != item.Item1))
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 701358fd4..05fb08447 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
// No biggie here. Nothing to delete
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index f98b09659..d5a7ccadb 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
progress.Report(100);
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
public string Key
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
index 032fa05a0..fbc3a309e 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
@@ -58,11 +58,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
progress.Report(0);
- LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
+ return LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
? LogSeverity.Debug
- : LogSeverity.Info);
-
- return Task.FromResult(true);
+ : LogSeverity.Info, cancellationToken);
}
/// <summary>
@@ -71,7 +69,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <value>The name.</value>
public string Name
{
- get { return "Start new log file"; }
+ get { return "Rotate log file"; }
}
public string Key { get; }
@@ -96,7 +94,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public bool IsHidden
{
- get { return true; }
+ get { return false; }
}
public bool IsEnabled
diff --git a/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs
index 1a944ebf2..82b449917 100644
--- a/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs
@@ -24,12 +24,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
public DayOfWeek DayOfWeek { get; set; }
/// <summary>
- /// Gets the execution properties of this task.
+ /// Gets or sets the options of this task.
/// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- public TaskExecutionOptions TaskOptions { get; set; }
+ public TaskOptions TaskOptions { get; set; }
/// <summary>
/// Gets or sets the timer.
@@ -100,7 +97,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Occurs when [triggered].
/// </summary>
- public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
+ public event EventHandler<EventArgs> Triggered;
/// <summary>
/// Called when [triggered].
@@ -109,7 +106,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (Triggered != null)
{
- Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
+ Triggered(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index b1877d776..45f7f1e95 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -11,19 +11,21 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using SQLitePCL.pretty;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Devices;
namespace Emby.Server.Implementations.Security
{
public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
{
- private readonly IServerApplicationPaths _appPaths;
+ private readonly IServerConfigurationManager _config;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public AuthenticationRepository(ILogger logger, IServerApplicationPaths appPaths)
+ public AuthenticationRepository(ILogger logger, IServerConfigurationManager config)
: base(logger)
{
- _appPaths = appPaths;
- DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db");
+ _config = config;
+ DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
}
public void Initialize()
@@ -32,99 +34,171 @@ namespace Emby.Server.Implementations.Security
{
RunDefaultInitialization(connection);
+ var tableNewlyCreated = !TableExists(connection, "Tokens");
+
string[] queries = {
- "create table if not exists AccessTokens (Id GUID PRIMARY KEY NOT NULL, AccessToken TEXT NOT NULL, DeviceId TEXT NOT NULL, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, DeviceName TEXT NOT NULL, UserId TEXT, IsActive BIT NOT NULL, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)",
- "create index if not exists idx_AccessTokens on AccessTokens(Id)"
+ "create table if not exists Tokens (Id INTEGER PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT NOT NULL, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, DeviceName TEXT NOT NULL, UserId TEXT, UserName TEXT, IsActive BIT NOT NULL, DateCreated DATETIME NOT NULL, DateLastActivity DATETIME NOT NULL)",
+ "create table if not exists Devices (Id TEXT NOT NULL PRIMARY KEY, CustomName TEXT, Capabilities TEXT)",
+
+ "drop index if exists idx_AccessTokens",
+ "drop index if exists Tokens1",
+ "drop index if exists Tokens2",
+ "create index if not exists Tokens3 on Tokens (AccessToken, DateLastActivity)",
+ "create index if not exists Tokens4 on Tokens (Id, DateLastActivity)",
+ "create index if not exists Devices1 on Devices (Id)"
};
connection.RunQueries(queries);
- connection.RunInTransaction(db =>
+ TryMigrate(connection, tableNewlyCreated);
+ }
+ }
+
+ private void TryMigrate(ManagedConnection connection, bool tableNewlyCreated)
+ {
+ try
+ {
+ if (tableNewlyCreated && TableExists(connection, "AccessTokens"))
{
- var existingColumnNames = GetColumnNames(db, "AccessTokens");
+ connection.RunInTransaction(db =>
+ {
+ var existingColumnNames = GetColumnNames(db, "AccessTokens");
+
+ AddColumn(db, "AccessTokens", "UserName", "TEXT", existingColumnNames);
+ AddColumn(db, "AccessTokens", "DateLastActivity", "DATETIME", existingColumnNames);
+ AddColumn(db, "AccessTokens", "AppVersion", "TEXT", existingColumnNames);
- AddColumn(db, "AccessTokens", "AppVersion", "TEXT", existingColumnNames);
+ }, TransactionMode);
- }, TransactionMode);
+ connection.RunQueries(new[]
+ {
+ "update accesstokens set DateLastActivity=DateCreated where DateLastActivity is null",
+ "update accesstokens set DeviceName='Unknown' where DeviceName is null",
+ "update accesstokens set AppName='Unknown' where AppName is null",
+ "update accesstokens set AppVersion='1' where AppVersion is null",
+ "INSERT INTO Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) SELECT AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity FROM AccessTokens where deviceid not null and devicename not null and appname not null and isactive=1"
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error migrating authentication database", ex);
}
}
- public void Create(AuthenticationInfo info, CancellationToken cancellationToken)
+ public void Create(AuthenticationInfo info)
{
- info.Id = Guid.NewGuid().ToString("N");
+ if (info == null)
+ {
+ throw new ArgumentNullException("info");
+ }
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)"))
+ {
+ statement.TryBind("@AccessToken", info.AccessToken);
+
+ statement.TryBind("@DeviceId", info.DeviceId);
+ statement.TryBind("@AppName", info.AppName);
+ statement.TryBind("@AppVersion", info.AppVersion);
+ statement.TryBind("@DeviceName", info.DeviceName);
+ statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
+ statement.TryBind("@UserName", info.UserName);
+ statement.TryBind("@IsActive", true);
+ statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
+ statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
+
+ statement.MoveNext();
+ }
- Update(info, cancellationToken);
+ }, TransactionMode);
+ }
+ }
}
- public void Update(AuthenticationInfo info, CancellationToken cancellationToken)
+ public void Update(AuthenticationInfo info)
{
if (info == null)
{
- throw new ArgumentNullException("info");
+ throw new ArgumentNullException("entry");
}
- cancellationToken.ThrowIfCancellationRequested();
-
using (WriteLock.Write())
{
using (var connection = CreateConnection())
{
connection.RunInTransaction(db =>
{
- using (var statement = db.PrepareStatement("replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)"))
+ using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id"))
{
- statement.TryBind("@Id", info.Id.ToGuidBlob());
+ statement.TryBind("@Id", info.Id);
+
statement.TryBind("@AccessToken", info.AccessToken);
statement.TryBind("@DeviceId", info.DeviceId);
statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName);
- statement.TryBind("@UserId", info.UserId);
- statement.TryBind("@IsActive", info.IsActive);
+ statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
+ statement.TryBind("@UserName", info.UserName);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
-
- if (info.DateRevoked.HasValue)
- {
- statement.TryBind("@DateRevoked", info.DateRevoked.Value.ToDateTimeParamValue());
- }
- else
- {
- statement.TryBindNull("@DateRevoked");
- }
+ statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
statement.MoveNext();
}
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public void Delete(AuthenticationInfo info)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException("entry");
+ }
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id"))
+ {
+ statement.TryBind("@Id", info.Id);
+
+ statement.MoveNext();
+ }
}, TransactionMode);
}
}
}
- private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens";
+ private const string BaseSelectText = "select Tokens.Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, DateCreated, DateLastActivity, Devices.CustomName from Tokens left join Devices on Tokens.DeviceId=Devices.Id";
private void BindAuthenticationQueryParams(AuthenticationInfoQuery query, IStatement statement)
{
- if (!string.IsNullOrWhiteSpace(query.AccessToken))
+ if (!string.IsNullOrEmpty(query.AccessToken))
{
statement.TryBind("@AccessToken", query.AccessToken);
}
- if (!string.IsNullOrWhiteSpace(query.UserId))
+ if (!query.UserId.Equals(Guid.Empty))
{
- statement.TryBind("@UserId", query.UserId);
+ statement.TryBind("@UserId", query.UserId.ToString("N"));
}
- if (!string.IsNullOrWhiteSpace(query.DeviceId))
+ if (!string.IsNullOrEmpty(query.DeviceId))
{
statement.TryBind("@DeviceId", query.DeviceId);
}
-
- if (query.IsActive.HasValue)
- {
- statement.TryBind("@IsActive", query.IsActive.Value);
- }
}
public QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query)
@@ -138,26 +212,19 @@ namespace Emby.Server.Implementations.Security
var whereClauses = new List<string>();
- var startIndex = query.StartIndex ?? 0;
-
- if (!string.IsNullOrWhiteSpace(query.AccessToken))
+ if (!string.IsNullOrEmpty(query.AccessToken))
{
whereClauses.Add("AccessToken=@AccessToken");
}
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- whereClauses.Add("UserId=@UserId");
- }
-
- if (!string.IsNullOrWhiteSpace(query.DeviceId))
+ if (!string.IsNullOrEmpty(query.DeviceId))
{
whereClauses.Add("DeviceId=@DeviceId");
}
- if (query.IsActive.HasValue)
+ if (!query.UserId.Equals(Guid.Empty))
{
- whereClauses.Add("IsActive=@IsActive");
+ whereClauses.Add("UserId=@UserId");
}
if (query.HasUser.HasValue)
@@ -176,28 +243,23 @@ namespace Emby.Server.Implementations.Security
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
- if (startIndex > 0)
- {
- var pagingWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " 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,
- startIndex.ToString(_usCulture)));
- }
+ commandText += whereTextWithoutPaging;
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count));
+ commandText += " ORDER BY DateLastActivity desc";
- commandText += whereText;
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- commandText += " ORDER BY DateCreated";
+ if (query.Limit.HasValue || offset > 0)
+ {
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.Limit.HasValue)
- {
- commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ if (offset > 0)
+ {
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
+ }
}
var list = new List<AuthenticationInfo>();
@@ -212,7 +274,7 @@ namespace Emby.Server.Implementations.Security
var statementTexts = new List<string>();
statementTexts.Add(commandText);
- statementTexts.Add("select count (Id) from AccessTokens" + whereTextWithoutPaging);
+ statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
var statements = PrepareAllSafe(db, statementTexts)
.ToList();
@@ -244,38 +306,11 @@ namespace Emby.Server.Implementations.Security
}
}
- public AuthenticationInfo Get(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = BaseSelectText + " where Id=@Id";
-
- using (var statement = connection.PrepareStatement(commandText))
- {
- statement.BindParameters["@Id"].Bind(id.ToGuidBlob());
-
- foreach (var row in statement.ExecuteQuery())
- {
- return Get(row);
- }
- return null;
- }
- }
- }
- }
-
private AuthenticationInfo Get(IReadOnlyList<IResultSetValue> reader)
{
var info = new AuthenticationInfo
{
- Id = reader[0].ReadGuidFromBlob().ToString("N"),
+ Id = reader[0].ToInt64(),
AccessToken = reader[1].ToString()
};
@@ -301,18 +336,95 @@ namespace Emby.Server.Implementations.Security
if (reader[6].SQLiteType != SQLiteType.Null)
{
- info.UserId = reader[6].ToString();
+ info.UserId = new Guid(reader[6].ToString());
+ }
+
+ if (reader[7].SQLiteType != SQLiteType.Null)
+ {
+ info.UserName = reader[7].ToString();
}
- info.IsActive = reader[7].ToBool();
info.DateCreated = reader[8].ReadDateTime();
if (reader[9].SQLiteType != SQLiteType.Null)
{
- info.DateRevoked = reader[9].ReadDateTime();
+ info.DateLastActivity = reader[9].ReadDateTime();
+ }
+ else
+ {
+ info.DateLastActivity = info.DateCreated;
+ }
+
+ if (reader[10].SQLiteType != SQLiteType.Null)
+ {
+ info.DeviceName = reader[10].ToString();
}
return info;
}
+
+ public DeviceOptions GetDeviceOptions(string deviceId)
+ {
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ return connection.RunInTransaction(db =>
+ {
+ using (var statement = PrepareStatementSafe(db, "select CustomName from Devices where Id=@DeviceId"))
+ {
+ statement.TryBind("@DeviceId", deviceId);
+
+ var result = new DeviceOptions();
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ if (row[0].SQLiteType != SQLiteType.Null)
+ {
+ result.CustomName = row[0].ToString();
+ }
+ }
+
+ return result;
+ }
+
+ }, ReadTransactionMode);
+ }
+ }
+ }
+
+ public void UpdateDeviceOptions(string deviceId, DeviceOptions options)
+ {
+ if (options == null)
+ {
+ throw new ArgumentNullException("options");
+ }
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))"))
+ {
+ statement.TryBind("@Id", deviceId);
+
+ if (string.IsNullOrWhiteSpace(options.CustomName))
+ {
+ statement.TryBindNull("@CustomName");
+ }
+ else
+ {
+ statement.TryBind("@CustomName", options.CustomName);
+ }
+
+ statement.MoveNext();
+ }
+
+ }, TransactionMode);
+ }
+ }
+ }
}
}
diff --git a/Emby.Server.Implementations/Security/MBLicenseFile.cs b/Emby.Server.Implementations/Security/MBLicenseFile.cs
index dc0e8b161..1810cbcd2 100644
--- a/Emby.Server.Implementations/Security/MBLicenseFile.cs
+++ b/Emby.Server.Implementations/Security/MBLicenseFile.cs
@@ -22,12 +22,8 @@ namespace Emby.Server.Implementations.Security
get { return _regKey; }
set
{
- if (value != _regKey)
- {
- //if key is changed - clear out our saved validations
- _updateRecords.Clear();
- _regKey = value;
- }
+ _updateRecords.Clear();
+ _regKey = value;
}
}
@@ -114,14 +110,14 @@ namespace Emby.Server.Implementations.Security
{
lock (_fileLock)
{
- _fileSystem.WriteAllBytes(licenseFile, new byte[] { });
+ _fileSystem.WriteAllBytes(licenseFile, Array.Empty<byte>());
}
}
catch (IOException)
{
lock (_fileLock)
{
- _fileSystem.WriteAllBytes(licenseFile, new byte[] { });
+ _fileSystem.WriteAllBytes(licenseFile, Array.Empty<byte>());
}
}
}
diff --git a/Emby.Server.Implementations/Security/PluginSecurityManager.cs b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
index 615ffa1f4..c9c68703e 100644
--- a/Emby.Server.Implementations/Security/PluginSecurityManager.cs
+++ b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
@@ -26,30 +26,11 @@ namespace Emby.Server.Implementations.Security
private const string MBValidateUrl = "https://mb3admin.com/admin/service/registration/validate";
private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "https://mb3admin.com/admin/service/appstore/register";
- /// <summary>
- /// The _is MB supporter
- /// </summary>
- private bool? _isMbSupporter;
- /// <summary>
- /// The _is MB supporter initialized
- /// </summary>
- private bool _isMbSupporterInitialized;
- /// <summary>
- /// The _is MB supporter sync lock
- /// </summary>
- private object _isMbSupporterSyncLock = new object();
-
- /// <summary>
- /// Gets a value indicating whether this instance is MB supporter.
- /// </summary>
- /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value>
- public bool IsMBSupporter
+ public async Task<bool> IsSupporter()
{
- get
- {
- LazyInitializer.EnsureInitialized(ref _isMbSupporter, ref _isMbSupporterInitialized, ref _isMbSupporterSyncLock, () => GetSupporterRegistrationStatus().Result.IsRegistered);
- return _isMbSupporter.Value;
- }
+ var result = await GetRegistrationStatusInternal("MBSupporter", false, _appHost.ApplicationVersion.ToString(), CancellationToken.None).ConfigureAwait(false);
+
+ return result.IsRegistered;
}
private MBLicenseFile _licenseFile;
@@ -66,15 +47,6 @@ namespace Emby.Server.Implementations.Security
private readonly IFileSystem _fileSystem;
private readonly ICryptoProvider _cryptographyProvider;
- private IEnumerable<IRequiresRegistration> _registeredEntities;
- protected IEnumerable<IRequiresRegistration> RegisteredEntities
- {
- get
- {
- return _registeredEntities ?? (_registeredEntities = _appHost.GetExports<IRequiresRegistration>());
- }
- }
-
/// <summary>
/// Initializes a new instance of the <see cref="PluginSecurityManager" /> class.
/// </summary>
@@ -96,45 +68,12 @@ namespace Emby.Server.Implementations.Security
}
/// <summary>
- /// Load all registration info for all entities that require registration
- /// </summary>
- /// <returns></returns>
- public async Task LoadAllRegistrationInfo()
- {
- var tasks = new List<Task>();
-
- ResetSupporterInfo();
- tasks.AddRange(RegisteredEntities.Select(i => i.LoadRegistrationInfoAsync()));
- await Task.WhenAll(tasks);
- }
-
- /// <summary>
/// Gets the registration status.
/// This overload supports existing plug-ins.
/// </summary>
- /// <param name="feature">The feature.</param>
- /// <param name="mb2Equivalent">The MB2 equivalent.</param>
- /// <returns>Task{MBRegistrationRecord}.</returns>
- public Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent = null)
- {
- return GetRegistrationStatusInternal(feature, mb2Equivalent);
- }
-
- /// <summary>
- /// Gets the registration status.
- /// </summary>
- /// <param name="feature">The feature.</param>
- /// <param name="mb2Equivalent">The MB2 equivalent.</param>
- /// <param name="version">The version of this feature</param>
- /// <returns>Task{MBRegistrationRecord}.</returns>
- public Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent, string version)
- {
- return GetRegistrationStatusInternal(feature, mb2Equivalent, version);
- }
-
- private Task<MBRegistrationRecord> GetSupporterRegistrationStatus()
+ public Task<MBRegistrationRecord> GetRegistrationStatus(string feature)
{
- return GetRegistrationStatusInternal("MBSupporter", null, _appHost.ApplicationVersion.ToString());
+ return GetRegistrationStatusInternal(feature, false, null, CancellationToken.None);
}
/// <summary>
@@ -149,20 +88,24 @@ namespace Emby.Server.Implementations.Security
}
set
{
- var newValue = value;
- if (newValue != null)
- {
- newValue = newValue.Trim();
- }
+ throw new Exception("Please call UpdateSupporterKey");
+ }
+ }
- if (newValue != LicenseFile.RegKey)
- {
- LicenseFile.RegKey = newValue;
- LicenseFile.Save();
+ public async Task UpdateSupporterKey(string newValue)
+ {
+ if (newValue != null)
+ {
+ newValue = newValue.Trim();
+ }
- // re-load registration info
- Task.Run(() => LoadAllRegistrationInfo());
- }
+ if (!string.Equals(newValue, LicenseFile.RegKey, StringComparison.Ordinal))
+ {
+ LicenseFile.RegKey = newValue;
+ LicenseFile.Save();
+
+ // Reset this
+ await GetRegistrationStatusInternal("MBSupporter", true, _appHost.ApplicationVersion.ToString(), CancellationToken.None).ConfigureAwait(false);
}
}
@@ -187,7 +130,7 @@ namespace Emby.Server.Implementations.Security
{
using (var response = await _httpClient.Post(options).ConfigureAwait(false))
{
- var reg = _jsonSerializer.DeserializeFromStream<RegRecord>(response.Content);
+ var reg = await _jsonSerializer.DeserializeFromStreamAsync<RegRecord>(response.Content).ConfigureAwait(false);
if (reg == null)
{
@@ -197,7 +140,7 @@ namespace Emby.Server.Implementations.Security
}
if (!String.IsNullOrEmpty(reg.key))
{
- SupporterKey = reg.key;
+ await UpdateSupporterKey(reg.key).ConfigureAwait(false);
}
}
@@ -241,97 +184,113 @@ namespace Emby.Server.Implementations.Security
}
}
- private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature,
- string mb2Equivalent = null,
- string version = null)
- {
- var regInfo = LicenseFile.GetRegInfo(feature);
- var lastChecked = regInfo == null ? DateTime.MinValue : regInfo.LastChecked;
- var expDate = regInfo == null ? DateTime.MinValue : regInfo.ExpirationDate;
+ private SemaphoreSlim _regCheckLock = new SemaphoreSlim(1, 1);
- var maxCacheDays = 14;
- var nextCheckDate = new [] { expDate, lastChecked.AddDays(maxCacheDays) }.Min();
+ private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature, bool forceCallToServer, string version, CancellationToken cancellationToken)
+ {
+ await _regCheckLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- if (nextCheckDate > DateTime.UtcNow.AddDays(maxCacheDays))
+ try
{
- nextCheckDate = DateTime.MinValue;
- }
+ var regInfo = LicenseFile.GetRegInfo(feature);
+ var lastChecked = regInfo == null ? DateTime.MinValue : regInfo.LastChecked;
+ var expDate = regInfo == null ? DateTime.MinValue : regInfo.ExpirationDate;
- //check the reg file first to alleviate strain on the MB admin server - must actually check in every 30 days tho
- var reg = new RegRecord
- {
- // Cache the result for up to a week
- registered = regInfo != null && nextCheckDate >= DateTime.UtcNow && expDate >= DateTime.UtcNow,
- expDate = expDate
- };
+ var maxCacheDays = 14;
+ var nextCheckDate = new[] { expDate, lastChecked.AddDays(maxCacheDays) }.Min();
- var success = reg.registered;
+ if (nextCheckDate > DateTime.UtcNow.AddDays(maxCacheDays))
+ {
+ nextCheckDate = DateTime.MinValue;
+ }
- if (!(lastChecked > DateTime.UtcNow.AddDays(-1)) || !reg.registered)
- {
- var data = new Dictionary<string, string>
+ //check the reg file first to alleviate strain on the MB admin server - must actually check in every 30 days tho
+ var reg = new RegRecord
{
- { "feature", feature },
- { "key", SupporterKey },
- { "mac", _appHost.SystemId },
- { "systemid", _appHost.SystemId },
- { "mb2equiv", mb2Equivalent },
- { "ver", version },
- { "platform", _appHost.OperatingSystemDisplayName }
+ // Cache the result for up to a week
+ registered = regInfo != null && nextCheckDate >= DateTime.UtcNow && expDate >= DateTime.UtcNow,
+ expDate = expDate
};
- try
+ var key = SupporterKey;
+
+ if (!forceCallToServer && string.IsNullOrWhiteSpace(key))
{
- var options = new HttpRequestOptions
- {
- Url = MBValidateUrl,
+ return new MBRegistrationRecord();
+ }
- // Seeing block length errors
- EnableHttpCompression = false,
- BufferContent = false
- };
+ var success = reg.registered;
- options.SetPostData(data);
+ if (!(lastChecked > DateTime.UtcNow.AddDays(-1)) || (!reg.registered))
+ {
+ var data = new Dictionary<string, string>
+ {
+ { "feature", feature },
+ { "key", key },
+ { "mac", _appHost.SystemId },
+ { "systemid", _appHost.SystemId },
+ { "ver", version },
+ { "platform", _appHost.OperatingSystemDisplayName }
+ };
- using (var response = (await _httpClient.Post(options).ConfigureAwait(false)))
+ try
{
- using (var json = response.Content)
+ var options = new HttpRequestOptions
{
- reg = _jsonSerializer.DeserializeFromStream<RegRecord>(json);
- success = true;
+ Url = MBValidateUrl,
+
+ // Seeing block length errors
+ EnableHttpCompression = false,
+ BufferContent = false,
+ CancellationToken = cancellationToken
+ };
+
+ options.SetPostData(data);
+
+ using (var response = (await _httpClient.Post(options).ConfigureAwait(false)))
+ {
+ using (var json = response.Content)
+ {
+ reg = await _jsonSerializer.DeserializeFromStreamAsync<RegRecord>(json).ConfigureAwait(false);
+ success = true;
+ }
+ }
+
+ if (reg.registered)
+ {
+ _logger.Info("Registered for feature {0}", feature);
+ LicenseFile.AddRegCheck(feature, reg.expDate);
+ }
+ else
+ {
+ _logger.Info("Not registered for feature {0}", feature);
+ LicenseFile.RemoveRegCheck(feature);
}
- }
- if (reg.registered)
- {
- _logger.Info("Registered for feature {0}", feature);
- LicenseFile.AddRegCheck(feature, reg.expDate);
}
- else
+ catch (Exception e)
{
- _logger.Info("Not registered for feature {0}", feature);
- LicenseFile.RemoveRegCheck(feature);
+ _logger.ErrorException("Error checking registration status of {0}", e, feature);
}
-
- }
- catch (Exception e)
- {
- _logger.ErrorException("Error checking registration status of {0}", e, feature);
}
- }
- var record = new MBRegistrationRecord
- {
- IsRegistered = reg.registered,
- ExpirationDate = reg.expDate,
- RegChecked = true,
- RegError = !success
- };
+ var record = new MBRegistrationRecord
+ {
+ IsRegistered = reg.registered,
+ ExpirationDate = reg.expDate,
+ RegChecked = true,
+ RegError = !success
+ };
- record.TrialVersion = IsInTrial(reg.expDate, record.RegChecked, record.IsRegistered);
- record.IsValid = !record.RegChecked || record.IsRegistered || record.TrialVersion;
+ record.TrialVersion = IsInTrial(reg.expDate, record.RegChecked, record.IsRegistered);
+ record.IsValid = !record.RegChecked || record.IsRegistered || record.TrialVersion;
- return record;
+ return record;
+ }
+ finally
+ {
+ _regCheckLock.Release();
+ }
}
private bool IsInTrial(DateTime expirationDate, bool regChecked, bool isRegistered)
@@ -346,14 +305,5 @@ namespace Emby.Server.Implementations.Security
return isInTrial && !isRegistered;
}
-
- /// <summary>
- /// Resets the supporter info.
- /// </summary>
- private void ResetSupporterInfo()
- {
- _isMbSupporter = null;
- _isMbSupporterInitialized = false;
- }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
index c9db33689..26371d21d 100644
--- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
@@ -3,6 +3,7 @@ using System.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
+using System.Threading.Tasks;
namespace Emby.Common.Implementations.Serialization
{
@@ -60,7 +61,7 @@ namespace Emby.Common.Implementations.Serialization
throw new ArgumentNullException("file");
}
- using (Stream stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (Stream stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
SerializeToStream(obj, stream);
}
@@ -68,7 +69,7 @@ namespace Emby.Common.Implementations.Serialization
private Stream OpenFile(string path)
{
- _logger.Debug("Deserializing file {0}", path);
+ //_logger.Debug("Deserializing file {0}", path);
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072);
}
@@ -135,6 +136,21 @@ namespace Emby.Common.Implementations.Serialization
return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream);
}
+ public async Task<T> DeserializeFromStreamAsync<T>(Stream stream)
+ {
+ if (stream == null)
+ {
+ throw new ArgumentNullException("stream");
+ }
+
+ using (var reader = new StreamReader(stream))
+ {
+ var json = await reader.ReadToEndAsync().ConfigureAwait(false);
+
+ return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(json);
+ }
+ }
+
/// <summary>
/// Deserializes from string.
/// </summary>
@@ -174,6 +190,26 @@ namespace Emby.Common.Implementations.Serialization
return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream);
}
+ public async Task<object> DeserializeFromStreamAsync(Stream stream, Type type)
+ {
+ if (stream == null)
+ {
+ throw new ArgumentNullException("stream");
+ }
+
+ if (type == null)
+ {
+ throw new ArgumentNullException("type");
+ }
+
+ using (var reader = new StreamReader(stream))
+ {
+ var json = await reader.ReadToEndAsync().ConfigureAwait(false);
+
+ return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type);
+ }
+ }
+
/// <summary>
/// Configures this instance.
/// </summary>
@@ -184,6 +220,18 @@ namespace Emby.Common.Implementations.Serialization
ServiceStack.Text.JsConfig.IncludeNullValues = false;
ServiceStack.Text.JsConfig.AlwaysUseUtc = true;
ServiceStack.Text.JsConfig.AssumeUtc = true;
+
+ ServiceStack.Text.JsConfig<Guid>.SerializeFn = SerializeGuid;
+ }
+
+ private string SerializeGuid(Guid guid)
+ {
+ if (guid.Equals(Guid.Empty))
+ {
+ return null;
+ }
+
+ return guid.ToString("N");
}
/// <summary>
diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs
index 3e3f7e0d7..1686a548b 100644
--- a/Emby.Server.Implementations/ServerApplicationPaths.cs
+++ b/Emby.Server.Implementations/ServerApplicationPaths.cs
@@ -58,26 +58,6 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// The _ibn path
- /// </summary>
- private string _ibnPath;
- /// <summary>
- /// Gets the path to the Images By Name directory
- /// </summary>
- /// <value>The images by name path.</value>
- public string ItemsByNamePath
- {
- get
- {
- return _ibnPath ?? (_ibnPath = Path.Combine(ProgramDataPath, "ImagesByName"));
- }
- set
- {
- _ibnPath = value;
- }
- }
-
- /// <summary>
/// Gets the path to the People directory
/// </summary>
/// <value>The people path.</value>
@@ -85,7 +65,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "People");
+ return Path.Combine(InternalMetadataPath, "People");
}
}
@@ -93,7 +73,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "artists");
+ return Path.Combine(InternalMetadataPath, "artists");
}
}
@@ -105,7 +85,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "Genre");
+ return Path.Combine(InternalMetadataPath, "Genre");
}
}
@@ -117,7 +97,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "MusicGenre");
+ return Path.Combine(InternalMetadataPath, "MusicGenre");
}
}
@@ -129,7 +109,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "Studio");
+ return Path.Combine(InternalMetadataPath, "Studio");
}
}
@@ -141,7 +121,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "Year");
+ return Path.Combine(InternalMetadataPath, "Year");
}
}
@@ -153,7 +133,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "general");
+ return Path.Combine(InternalMetadataPath, "general");
}
}
@@ -165,7 +145,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "ratings");
+ return Path.Combine(InternalMetadataPath, "ratings");
}
}
@@ -177,7 +157,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "mediainfo");
+ return Path.Combine(InternalMetadataPath, "mediainfo");
}
}
@@ -193,12 +173,21 @@ namespace Emby.Server.Implementations
}
}
+ private string _defaultTranscodingTempPath;
+ public string DefaultTranscodingTempPath
+ {
+ get
+ {
+ return _defaultTranscodingTempPath ?? (_defaultTranscodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp"));
+ }
+ }
+
private string _transcodingTempPath;
public string TranscodingTempPath
{
get
{
- return _transcodingTempPath ?? (_transcodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp"));
+ return _transcodingTempPath ?? (_transcodingTempPath = DefaultTranscodingTempPath);
}
set
{
@@ -210,17 +199,26 @@ namespace Emby.Server.Implementations
{
var path = TranscodingTempPath;
- try
- {
- Directory.CreateDirectory(path);
- return path;
- }
- catch
+ if (!string.Equals(path, DefaultTranscodingTempPath, StringComparison.OrdinalIgnoreCase))
{
- path = Path.Combine(ProgramDataPath, "transcoding-temp");
- Directory.CreateDirectory(path);
- return path;
+ try
+ {
+ Directory.CreateDirectory(path);
+
+ var testPath = Path.Combine(path, Guid.NewGuid().ToString());
+ Directory.CreateDirectory(testPath);
+ Directory.Delete(testPath);
+
+ return path;
+ }
+ catch
+ {
+ }
}
+
+ path = DefaultTranscodingTempPath;
+ Directory.CreateDirectory(path);
+ return path;
}
/// <summary>
@@ -231,7 +229,7 @@ namespace Emby.Server.Implementations
{
get
{
- return Path.Combine(ItemsByNamePath, "GameGenre");
+ return Path.Combine(InternalMetadataPath, "GameGenre");
}
}
@@ -247,5 +245,15 @@ namespace Emby.Server.Implementations
_internalMetadataPath = value;
}
}
+
+ private const string _virtualInternalMetadataPath = "%MetadataPath%";
+ public string VirtualInternalMetadataPath
+ {
+ get
+ {
+ return _virtualInternalMetadataPath;
+ }
+ }
+
}
}
diff --git a/Emby.Server.Implementations/ServerManager/ServerManager.cs b/Emby.Server.Implementations/ServerManager/ServerManager.cs
deleted file mode 100644
index b267f928b..000000000
--- a/Emby.Server.Implementations/ServerManager/ServerManager.cs
+++ /dev/null
@@ -1,357 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.Text;
-
-namespace Emby.Server.Implementations.ServerManager
-{
- /// <summary>
- /// Manages the Http Server, Udp Server and WebSocket connections
- /// </summary>
- public class ServerManager : IServerManager
- {
- /// <summary>
- /// Both the Ui and server will have a built-in HttpServer.
- /// People will inevitably want remote control apps so it's needed in the Ui too.
- /// </summary>
- /// <value>The HTTP server.</value>
- private IHttpServer HttpServer { get; set; }
-
- /// <summary>
- /// Gets or sets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// The web socket connections
- /// </summary>
- private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
- /// <summary>
- /// Gets the web socket connections.
- /// </summary>
- /// <value>The web socket connections.</value>
- public IEnumerable<IWebSocketConnection> WebSocketConnections
- {
- get { return _webSocketConnections; }
- }
-
- public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _application host
- /// </summary>
- private readonly IServerApplicationHost _applicationHost;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- /// <summary>
- /// Gets the web socket listeners.
- /// </summary>
- /// <value>The web socket listeners.</value>
- private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>();
-
- private bool _disposed;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
- private readonly ITextEncoding _textEncoding;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerManager" /> class.
- /// </summary>
- /// <param name="applicationHost">The application host.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <exception cref="System.ArgumentNullException">applicationHost</exception>
- public ServerManager(IServerApplicationHost applicationHost, IJsonSerializer jsonSerializer, ILogger logger, IServerConfigurationManager configurationManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding)
- {
- if (applicationHost == null)
- {
- throw new ArgumentNullException("applicationHost");
- }
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException("jsonSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _applicationHost = applicationHost;
- ConfigurationManager = configurationManager;
- _memoryStreamProvider = memoryStreamProvider;
- _textEncoding = textEncoding;
- }
-
- /// <summary>
- /// Starts this instance.
- /// </summary>
- public void Start(string[] urlPrefixes)
- {
- ReloadHttpServer(urlPrefixes);
- }
-
- /// <summary>
- /// Restarts the Http Server, or starts it if not currently running
- /// </summary>
- private void ReloadHttpServer(string[] urlPrefixes)
- {
- _logger.Info("Loading Http Server");
-
- try
- {
- HttpServer = _applicationHost.Resolve<IHttpServer>();
- HttpServer.StartServer(urlPrefixes);
- }
- catch (Exception ex)
- {
- var msg = string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase)
- ? "The http server is unable to start due to a Socket error. This can occasionally happen when the operating system takes longer than usual to release the IP bindings from the previous session. This can take up to five minutes. Please try waiting or rebooting the system."
- : "Error starting Http Server";
-
- _logger.ErrorException(msg, ex);
-
- throw;
- }
-
- HttpServer.WebSocketConnected += HttpServer_WebSocketConnected;
- }
-
- /// <summary>
- /// Handles the WebSocketConnected event of the HttpServer control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="WebSocketConnectEventArgs" /> instance containing the event data.</param>
- void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e)
- {
- if (_disposed)
- {
- return;
- }
-
- var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger, _memoryStreamProvider, _textEncoding)
- {
- OnReceive = ProcessWebSocketMessageReceived,
- Url = e.Url,
- QueryString = e.QueryString ?? new QueryParamCollection()
- };
-
- _webSocketConnections.Add(connection);
-
- if (WebSocketConnected != null)
- {
- EventHelper.FireEventIfNotNull(WebSocketConnected, this, new GenericEventArgs<IWebSocketConnection> (connection), _logger);
- }
- }
-
- /// <summary>
- /// Processes the web socket message received.
- /// </summary>
- /// <param name="result">The result.</param>
- private async void ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
- {
- if (_disposed)
- {
- return;
- }
-
- //_logger.Debug("Websocket message received: {0}", result.MessageType);
-
- var tasks = _webSocketListeners.Select(i => Task.Run(async () =>
- {
- try
- {
- await i.ProcessMessage(result).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed processing WebSocket message {1}", ex, i.GetType().Name, result.MessageType ?? string.Empty);
- }
- }));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="data">The data.</param>
- /// <returns>Task.</returns>
- public void SendWebSocketMessage<T>(string messageType, T data)
- {
- SendWebSocketMessage(messageType, () => data);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- public void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction)
- {
- SendWebSocketMessageAsync(messageType, dataFunction, CancellationToken.None);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType</exception>
- public Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken)
- {
- return SendWebSocketMessageAsync(messageType, dataFunction, _webSocketConnections, cancellationToken);
- }
-
- /// <summary>
- /// Sends the web socket message async.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The data function.</param>
- /// <param name="connections">The connections.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType
- /// or
- /// dataFunction
- /// or
- /// cancellationToken</exception>
- private async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(messageType))
- {
- throw new ArgumentNullException("messageType");
- }
-
- if (dataFunction == null)
- {
- throw new ArgumentNullException("dataFunction");
- }
-
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var connectionsList = connections.Where(s => s.State == WebSocketState.Open).ToList();
-
- if (connectionsList.Count > 0)
- {
- _logger.Info("Sending web socket message {0}", messageType);
-
- var message = new WebSocketMessage<T> { MessageType = messageType, Data = dataFunction() };
- var json = _jsonSerializer.SerializeToString(message);
-
- var tasks = connectionsList.Select(s => Task.Run(() =>
- {
- try
- {
- s.SendAsync(json, cancellationToken);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message {0} to {1}", ex, messageType, s.RemoteEndPoint);
- }
-
- }, cancellationToken));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Disposes the current HttpServer
- /// </summary>
- private void DisposeHttpServer()
- {
- _logger.Info("Disposing web socket connections");
- foreach (var socket in _webSocketConnections)
- {
- // Dispose the connection
- socket.Dispose();
- }
-
- _webSocketConnections.Clear();
-
- if (HttpServer != null)
- {
- HttpServer.WebSocketConnected -= HttpServer_WebSocketConnected;
-
- _logger.Info("Disposing http server");
-
- HttpServer.Dispose();
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- _disposed = true;
-
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- DisposeHttpServer();
- }
- }
-
- /// <summary>
- /// Adds the web socket listeners.
- /// </summary>
- /// <param name="listeners">The listeners.</param>
- public void AddWebSocketListeners(IEnumerable<IWebSocketListener> listeners)
- {
- _webSocketListeners.AddRange(listeners);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs
index 7538d3102..711ba8bbc 100644
--- a/Emby.Server.Implementations/Services/RequestHelper.cs
+++ b/Emby.Server.Implementations/Services/RequestHelper.cs
@@ -1,12 +1,13 @@
using System;
using System.IO;
using Emby.Server.Implementations.HttpServer;
+using System.Threading.Tasks;
namespace Emby.Server.Implementations.Services
{
public class RequestHelper
{
- public static Func<Type, Stream, object> GetRequestReader(HttpListenerHost host, string contentType)
+ public static Func<Type, Stream, Task<object>> GetRequestReader(HttpListenerHost host, string contentType)
{
switch (GetContentTypeWithoutEncoding(contentType))
{
diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs
index 22e1bc4aa..16de1a083 100644
--- a/Emby.Server.Implementations/Services/ResponseHelper.cs
+++ b/Emby.Server.Implementations/Services/ResponseHelper.cs
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Services
{
public static class ResponseHelper
{
- public static async Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
+ public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
{
if (result == null)
{
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services
}
response.SetContentLength(0);
- return;
+ return Task.CompletedTask;
}
var httpResult = result as IHttpResult;
@@ -46,18 +46,6 @@ namespace Emby.Server.Implementations.Services
// httpResult.ContentType = defaultContentType;
//}
//response.ContentType = httpResult.ContentType;
-
- if (httpResult.Cookies != null)
- {
- var httpRes = response as IHttpResponse;
- if (httpRes != null)
- {
- foreach (var cookie in httpResult.Cookies)
- {
- httpRes.SetCookie(cookie);
- }
- }
- }
}
var responseOptions = result as IHasHeaders;
@@ -90,32 +78,26 @@ namespace Emby.Server.Implementations.Services
var asyncStreamWriter = result as IAsyncStreamWriter;
if (asyncStreamWriter != null)
{
- await asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken).ConfigureAwait(false);
- return;
+ return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
}
var streamWriter = result as IStreamWriter;
if (streamWriter != null)
{
streamWriter.WriteTo(response.OutputStream);
- return;
+ return Task.CompletedTask;
}
var fileWriter = result as FileWriter;
if (fileWriter != null)
{
- await fileWriter.WriteToAsync(response, cancellationToken).ConfigureAwait(false);
- return;
+ return fileWriter.WriteToAsync(response, cancellationToken);
}
var stream = result as Stream;
if (stream != null)
{
- using (stream)
- {
- await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
- return;
- }
+ return CopyStream(stream, response.OutputStream);
}
var bytes = result as byte[];
@@ -126,9 +108,9 @@ namespace Emby.Server.Implementations.Services
if (bytes.Length > 0)
{
- await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
+ return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
}
- return;
+ return Task.CompletedTask;
}
var responseText = result as string;
@@ -138,12 +120,20 @@ namespace Emby.Server.Implementations.Services
response.SetContentLength(bytes.Length);
if (bytes.Length > 0)
{
- await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
+ return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
}
- return;
+ return Task.CompletedTask;
}
- await WriteObject(request, result, response).ConfigureAwait(false);
+ return WriteObject(request, result, response);
+ }
+
+ private static async Task CopyStream(Stream src, Stream dest)
+ {
+ using (src)
+ {
+ await src.CopyToAsync(dest).ConfigureAwait(false);
+ }
}
public static async Task WriteObject(IRequest request, object result, IResponse response)
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index 3fd6d88f8..3726c9f6b 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Services
// mi.ReturnType
// : Type.GetType(requestType.FullName + "Response");
- RegisterRestPaths(appHost, requestType);
+ RegisterRestPaths(appHost, requestType, serviceType);
appHost.AddServiceInfo(serviceType, requestType);
}
@@ -68,14 +68,14 @@ namespace Emby.Server.Implementations.Services
return null;
}
- public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>(StringComparer.OrdinalIgnoreCase);
+ public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
- public void RegisterRestPaths(HttpListenerHost appHost, Type requestType)
+ public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
{
var attrs = appHost.GetRouteAttributes(requestType);
foreach (RouteAttribute attr in attrs)
{
- var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
+ var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
RegisterRestPath(restPath);
}
@@ -114,19 +114,20 @@ namespace Emby.Server.Implementations.Services
}
var bestScore = -1;
+ RestPath bestMatch = null;
foreach (var restPath in firstMatches)
{
var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
- if (score > bestScore) bestScore = score;
+ if (score > bestScore)
+ {
+ bestScore = score;
+ bestMatch = restPath;
+ }
}
- if (bestScore > 0)
+ if (bestScore > 0 && bestMatch != null)
{
- foreach (var restPath in firstMatches)
- {
- if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
- return restPath;
- }
+ return bestMatch;
}
}
@@ -136,19 +137,21 @@ namespace Emby.Server.Implementations.Services
if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
var bestScore = -1;
+ RestPath bestMatch = null;
foreach (var restPath in firstMatches)
{
var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
- if (score > bestScore) bestScore = score;
- }
- if (bestScore > 0)
- {
- foreach (var restPath in firstMatches)
+ if (score > bestScore)
{
- if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
- return restPath;
+ bestScore = score;
+ bestMatch = restPath;
}
}
+
+ if (bestScore > 0 && bestMatch != null)
+ {
+ return bestMatch;
+ }
}
return null;
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index 5709d3e0a..79b57438c 100644
--- a/Emby.Server.Implementations/Services/ServiceExec.cs
+++ b/Emby.Server.Implementations/Services/ServiceExec.cs
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.Services
}
}
- public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
+ public static Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
{
var actionName = request.Verb ?? "POST";
@@ -82,7 +82,10 @@ namespace Emby.Server.Implementations.Services
foreach (var requestFilter in actionContext.RequestFilters)
{
requestFilter.RequestFilter(request, request.Response, requestDto);
- if (request.Response.IsClosed) return null;
+ if (request.Response.IsClosed)
+ {
+ Task.FromResult<object>(null);
+ }
}
}
@@ -91,17 +94,56 @@ namespace Emby.Server.Implementations.Services
var taskResponse = response as Task;
if (taskResponse != null)
{
- await taskResponse.ConfigureAwait(false);
- response = ServiceHandler.GetTaskResult(taskResponse);
+ return GetTaskResult(taskResponse);
}
- return response;
+ return Task.FromResult(response);
}
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower();
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
}
+ private static async Task<object> GetTaskResult(Task task)
+ {
+ try
+ {
+ var taskObject = task as Task<object>;
+ if (taskObject != null)
+ {
+ return await taskObject.ConfigureAwait(false);
+ }
+
+ await task.ConfigureAwait(false);
+
+ var type = task.GetType().GetTypeInfo();
+ if (!type.IsGenericType)
+ {
+ return null;
+ }
+
+ var resultProperty = type.GetDeclaredProperty("Result");
+ if (resultProperty == null)
+ {
+ return null;
+ }
+
+ var result = resultProperty.GetValue(task);
+
+ // hack alert
+ if (result.GetType().Name.IndexOf("voidtaskresult", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return null;
+ }
+
+ return result;
+ }
+ catch (TypeAccessException)
+ {
+ return null; //return null for void Task's
+ }
+ }
+
public static List<ServiceMethod> Reset(Type serviceType)
{
var actions = new List<ServiceMethod>();
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
index d500595ce..e76857a8d 100644
--- a/Emby.Server.Implementations/Services/ServiceHandler.cs
+++ b/Emby.Server.Implementations/Services/ServiceHandler.cs
@@ -11,55 +11,7 @@ namespace Emby.Server.Implementations.Services
{
public class ServiceHandler
{
- public async Task<object> HandleResponseAsync(object response)
- {
- var taskResponse = response as Task;
-
- if (taskResponse == null)
- {
- return response;
- }
-
- await taskResponse.ConfigureAwait(false);
-
- var taskResult = GetTaskResult(taskResponse);
-
- var subTask = taskResult as Task;
- if (subTask != null)
- {
- taskResult = GetTaskResult(subTask);
- }
-
- return taskResult;
- }
-
- internal static object GetTaskResult(Task task)
- {
- try
- {
- var taskObject = task as Task<object>;
- if (taskObject != null)
- {
- return taskObject.Result;
- }
-
- task.Wait();
-
- var type = task.GetType().GetTypeInfo();
- if (!type.IsGenericType)
- {
- return null;
- }
-
- return type.GetDeclaredProperty("Result").GetValue(task);
- }
- catch (TypeAccessException)
- {
- return null; //return null for void Task's
- }
- }
-
- protected static object CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
+ protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
{
if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
{
@@ -69,7 +21,7 @@ namespace Emby.Server.Implementations.Services
return deserializer(requestType, httpReq.InputStream);
}
}
- return host.CreateInstance(requestType);
+ return Task.FromResult(host.CreateInstance(requestType));
}
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
@@ -137,14 +89,11 @@ namespace Emby.Server.Implementations.Services
if (ResponseContentType != null)
httpReq.ResponseContentType = ResponseContentType;
- var request = httpReq.Dto = CreateRequest(appHost, httpReq, restPath, logger);
+ var request = httpReq.Dto = await CreateRequest(appHost, httpReq, restPath, logger).ConfigureAwait(false);
appHost.ApplyRequestFilters(httpReq, httpRes, request);
- var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
-
- //var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
- var response = rawResponse;
+ var response = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
// Apply response filters
foreach (var responseFilter in appHost.ResponseFilters)
@@ -155,38 +104,37 @@ namespace Emby.Server.Implementations.Services
await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
}
- public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
+ public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
{
var requestType = restPath.RequestType;
if (RequireqRequestStream(requestType))
{
// Used by IRequiresRequestStream
- var request = ServiceHandler.CreateRequest(httpReq, restPath, GetRequestParams(httpReq), host.CreateInstance(requestType));
+ var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
+ var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
var rawReq = (IRequiresRequestStream)request;
rawReq.RequestStream = httpReq.InputStream;
return rawReq;
}
+ else
+ {
+ var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
- var requestParams = GetFlattenedRequestParams(httpReq);
- return CreateRequest(host, httpReq, restPath, requestParams);
+ var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
+
+ return CreateRequest(httpReq, restPath, requestParams, requestDto);
+ }
}
- private static bool RequireqRequestStream(Type requestType)
+ public static bool RequireqRequestStream(Type requestType)
{
var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
}
- public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
- {
- var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType);
-
- return CreateRequest(httpReq, restPath, requestParams, requestDto);
- }
-
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
{
string contentType;
@@ -200,7 +148,7 @@ namespace Emby.Server.Implementations.Services
/// <summary>
/// Duplicate Params are given a unique key by appending a #1 suffix
/// </summary>
- private static Dictionary<string, string> GetRequestParams(IRequest request)
+ private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
{
var map = new Dictionary<string, string>();
@@ -224,7 +172,7 @@ namespace Emby.Server.Implementations.Services
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
{
- var formData = request.FormData;
+ var formData = await request.GetFormData().ConfigureAwait(false);
if (formData != null)
{
foreach (var name in formData.Keys)
@@ -258,7 +206,7 @@ namespace Emby.Server.Implementations.Services
/// <summary>
/// Duplicate params have their values joined together in a comma-delimited string
/// </summary>
- private static Dictionary<string, string> GetFlattenedRequestParams(IRequest request)
+ private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
{
var map = new Dictionary<string, string>();
@@ -270,7 +218,7 @@ namespace Emby.Server.Implementations.Services
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
{
- var formData = request.FormData;
+ var formData = await request.GetFormData().ConfigureAwait(false);
if (formData != null)
{
foreach (var name in formData.Keys)
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index 0ca36df19..282269e7b 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -48,6 +48,8 @@ namespace Emby.Server.Implementations.Services
public Type RequestType { get; private set; }
+ public Type ServiceType { get; private set; }
+
public string Path { get { return this.restPath; } }
public string Summary { get; private set; }
@@ -56,6 +58,11 @@ namespace Emby.Server.Implementations.Services
public int Priority { get; set; } //passed back to RouteAttribute
+ public IEnumerable<string> PathVariables
+ {
+ get { return this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e)); }
+ }
+
public static string[] GetPathPartsForMatching(string pathInfo)
{
return pathInfo.ToLower().Split(new[] { PathSeperatorChar }, StringSplitOptions.RemoveEmptyEntries);
@@ -93,9 +100,10 @@ namespace Emby.Server.Implementations.Services
return list;
}
- public RestPath(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, string path, string verbs, bool isHidden = false, string summary = null, string description = null)
+ public RestPath(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, Type serviceType, string path, string verbs, bool isHidden = false, string summary = null, string description = null)
{
this.RequestType = requestType;
+ this.ServiceType = serviceType;
this.Summary = summary;
this.IsHidden = isHidden;
this.Description = description;
@@ -558,5 +566,12 @@ namespace Emby.Server.Implementations.Services
return this.typeDeserializer.PopulateFromMap(fromInstance, requestKeyValuesMap);
}
+
+ public class RestPathMap : SortedDictionary<string, List<RestPath>>
+ {
+ public RestPathMap() : base(StringComparer.OrdinalIgnoreCase)
+ {
+ }
+ }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs
deleted file mode 100644
index fc2bdbd55..000000000
--- a/Emby.Server.Implementations/Services/SwaggerService.cs
+++ /dev/null
@@ -1,260 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.Services
-{
- [Route("/swagger", "GET", Summary = "Gets the swagger specifications")]
- [Route("/swagger.json", "GET", Summary = "Gets the swagger specifications")]
- public class GetSwaggerSpec : IReturn<SwaggerSpec>
- {
- }
-
- public class SwaggerSpec
- {
- public string swagger { get; set; }
- public string[] schemes { get; set; }
- public SwaggerInfo info { get; set; }
- public string host { get; set; }
- public string basePath { get; set; }
- public SwaggerTag[] tags { get; set; }
- public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; }
- public Dictionary<string, SwaggerDefinition> definitions { get; set; }
- public SwaggerComponents components { get; set; }
- }
-
- public class SwaggerComponents
- {
- public Dictionary<string, SwaggerSecurityScheme> securitySchemes { get; set; }
- }
-
- public class SwaggerSecurityScheme
- {
- public string name { get; set; }
- public string type { get; set; }
- public string @in { get; set; }
- }
-
- public class SwaggerInfo
- {
- public string description { get; set; }
- public string version { get; set; }
- public string title { get; set; }
- public string termsOfService { get; set; }
-
- public SwaggerConcactInfo contact { get; set; }
- }
-
- public class SwaggerConcactInfo
- {
- public string email { get; set; }
- public string name { get; set; }
- public string url { get; set; }
- }
-
- public class SwaggerTag
- {
- public string description { get; set; }
- public string name { get; set; }
- }
-
- public class SwaggerMethod
- {
- public string summary { get; set; }
- public string description { get; set; }
- public string[] tags { get; set; }
- public string operationId { get; set; }
- public string[] consumes { get; set; }
- public string[] produces { get; set; }
- public SwaggerParam[] parameters { get; set; }
- public Dictionary<string, SwaggerResponse> responses { get; set; }
- public Dictionary<string, string[]>[] security { get; set; }
- }
-
- public class SwaggerParam
- {
- public string @in { get; set; }
- public string name { get; set; }
- public string description { get; set; }
- public bool required { get; set; }
- public string type { get; set; }
- public string collectionFormat { get; set; }
- }
-
- public class SwaggerResponse
- {
- public string description { get; set; }
-
- // ex. "$ref":"#/definitions/Pet"
- public Dictionary<string, string> schema { get; set; }
- }
-
- public class SwaggerDefinition
- {
- public string type { get; set; }
- public Dictionary<string, SwaggerProperty> properties { get; set; }
- }
-
- public class SwaggerProperty
- {
- public string type { get; set; }
- public string format { get; set; }
- public string description { get; set; }
- public string[] @enum { get; set; }
- public string @default { get; set; }
- }
-
- public class SwaggerService : IService, IRequiresRequest
- {
- private SwaggerSpec _spec;
-
- public IRequest Request { get; set; }
-
- public object Get(GetSwaggerSpec request)
- {
- return _spec ?? (_spec = GetSpec());
- }
-
- private SwaggerSpec GetSpec()
- {
- string host = null;
- Uri uri;
- if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri))
- {
- host = uri.Host;
- }
-
- var securitySchemes = new Dictionary<string, SwaggerSecurityScheme>();
-
- securitySchemes["api_key"] = new SwaggerSecurityScheme
- {
- name = "api_key",
- type = "apiKey",
- @in = "query"
- };
-
- var spec = new SwaggerSpec
- {
- schemes = new[] { "http" },
- tags = GetTags(),
- swagger = "2.0",
- info = new SwaggerInfo
- {
- title = "Emby Server API",
- version = "1.0.0",
- description = "Explore the Emby Server API",
- contact = new SwaggerConcactInfo
- {
- name = "Emby Developer Community",
- url = "https://emby.media/community/index.php?/forum/47-developer-api"
- },
- termsOfService = "https://emby.media/terms"
- },
- paths = GetPaths(),
- definitions = GetDefinitions(),
- basePath = "/emby",
- host = host,
-
- components = new SwaggerComponents
- {
- securitySchemes = securitySchemes
- }
- };
-
- return spec;
- }
-
-
- private SwaggerTag[] GetTags()
- {
- return new SwaggerTag[] { };
- }
-
- private Dictionary<string, SwaggerDefinition> GetDefinitions()
- {
- return new Dictionary<string, SwaggerDefinition>();
- }
-
- private IDictionary<string, Dictionary<string, SwaggerMethod>> GetPaths()
- {
- var paths = new SortedDictionary<string, Dictionary<string, SwaggerMethod>>();
-
- var all = ServiceController.Instance.RestPathMap.OrderBy(i => i.Key, StringComparer.OrdinalIgnoreCase).ToList();
-
- foreach (var current in all)
- {
- foreach (var info in current.Value)
- {
- if (info.IsHidden)
- {
- continue;
- }
-
- if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
- if (info.Path.StartsWith("/emby", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- paths[info.Path] = GetPathInfo(info);
- }
- }
-
- return paths;
- }
-
- private Dictionary<string, SwaggerMethod> GetPathInfo(RestPath info)
- {
- var result = new Dictionary<string, SwaggerMethod>();
-
- foreach (var verb in info.Verbs)
- {
- var responses = new Dictionary<string, SwaggerResponse>
- {
- };
-
- responses["200"] = new SwaggerResponse
- {
- description = "OK"
- };
-
- var security = new List<Dictionary<string, string[]>>();
-
- var apiKeySecurity = new Dictionary<string, string[]>();
- apiKeySecurity["api_key"] = new string[] { };
-
- security.Add(apiKeySecurity);
-
- result[verb.ToLower()] = new SwaggerMethod
- {
- summary = info.Summary,
- description = info.Description,
- produces = new[]
- {
- "application/json"
- },
- consumes = new[]
- {
- "application/json"
- },
- operationId = info.RequestType.Name,
- tags = new string[] { },
-
- parameters = new SwaggerParam[] { },
-
- responses = responses,
-
- security = security.ToArray()
- };
- }
-
- return result;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
index c7346789a..ba9889c41 100644
--- a/Emby.Server.Implementations/Services/UrlExtensions.cs
+++ b/Emby.Server.Implementations/Services/UrlExtensions.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Services
return type.IsGenericParameter ? "'" + typeName : typeName;
}
- public static string LeftPart(string strVal, string needle)
+ private static string LeftPart(string strVal, string needle)
{
if (strVal == null) return null;
var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
diff --git a/Emby.Server.Implementations/Session/FirebaseSessionController.cs b/Emby.Server.Implementations/Session/FirebaseSessionController.cs
new file mode 100644
index 000000000..cfe513305
--- /dev/null
+++ b/Emby.Server.Implementations/Session/FirebaseSessionController.cs
@@ -0,0 +1,131 @@
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Text;
+using MediaBrowser.Common;
+
+namespace Emby.Server.Implementations.Session
+{
+ public class FirebaseSessionController : ISessionController
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly IJsonSerializer _json;
+ private readonly ISessionManager _sessionManager;
+
+ public SessionInfo Session { get; private set; }
+
+ private readonly string _token;
+
+ private IApplicationHost _appHost;
+ private string _senderId;
+ private string _applicationId;
+
+ public FirebaseSessionController(IHttpClient httpClient,
+ IApplicationHost appHost,
+ IJsonSerializer json,
+ SessionInfo session,
+ string token, ISessionManager sessionManager)
+ {
+ _httpClient = httpClient;
+ _json = json;
+ _appHost = appHost;
+ Session = session;
+ _token = token;
+ _sessionManager = sessionManager;
+
+ _applicationId = _appHost.GetValue("firebase_applicationid");
+ _senderId = _appHost.GetValue("firebase_senderid");
+ }
+
+ public static bool IsSupported(IApplicationHost appHost)
+ {
+ return !string.IsNullOrEmpty(appHost.GetValue("firebase_applicationid")) && !string.IsNullOrEmpty(appHost.GetValue("firebase_senderid"));
+ }
+
+ public bool IsSessionActive
+ {
+ get
+ {
+ return (DateTime.UtcNow - Session.LastActivityDate).TotalDays <= 3;
+ }
+ }
+
+ public bool SupportsMediaControl
+ {
+ get { return true; }
+ }
+
+ public async Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
+ {
+ if (!IsSessionActive)
+ {
+ return;
+ }
+
+ if (string.IsNullOrEmpty(_senderId) || string.IsNullOrEmpty(_applicationId))
+ {
+ return;
+ }
+
+ foreach (var controller in allControllers)
+ {
+ // Don't send if there's an active web socket connection
+ if ((controller is WebSocketController) && controller.IsSessionActive)
+ {
+ return;
+ }
+ }
+
+ var msg = new WebSocketMessage<T>
+ {
+ Data = data,
+ MessageType = name,
+ MessageId = messageId,
+ ServerId = _appHost.SystemId
+ };
+
+ var req = new FirebaseBody<T>
+ {
+ to = _token,
+ data = msg
+ };
+
+ var byteArray = Encoding.UTF8.GetBytes(_json.SerializeToString(req));
+
+ var enableLogging = false;
+
+#if DEBUG
+ enableLogging = true;
+#endif
+
+ var options = new HttpRequestOptions
+ {
+ Url = "https://fcm.googleapis.com/fcm/send",
+ RequestContentType = "application/json",
+ RequestContentBytes = byteArray,
+ CancellationToken = cancellationToken,
+ LogRequest = enableLogging,
+ LogResponse = enableLogging,
+ LogErrors = enableLogging
+ };
+
+ options.RequestHeaders["Authorization"] = string.Format("key={0}", _applicationId);
+ options.RequestHeaders["Sender"] = string.Format("id={0}", _senderId);
+
+ using (var response = await _httpClient.Post(options).ConfigureAwait(false))
+ {
+
+ }
+ }
+ }
+
+ internal class FirebaseBody<T>
+ {
+ public string to { get; set; }
+ public WebSocketMessage<T> data { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs
index 6725cd7af..ff9b3fefc 100644
--- a/Emby.Server.Implementations/Session/HttpSessionController.cs
+++ b/Emby.Server.Implementations/Session/HttpSessionController.cs
@@ -36,10 +36,6 @@ namespace Emby.Server.Implementations.Session
_sessionManager = sessionManager;
}
- public void OnActivity()
- {
- }
-
private string PostUrl
{
get
@@ -52,7 +48,7 @@ namespace Emby.Server.Implementations.Session
{
get
{
- return (DateTime.UtcNow - Session.LastActivityDate).TotalMinutes <= 10;
+ return (DateTime.UtcNow - Session.LastActivityDate).TotalMinutes <= 5;
}
}
@@ -61,49 +57,29 @@ namespace Emby.Server.Implementations.Session
get { return true; }
}
- private Task SendMessage(string name, CancellationToken cancellationToken)
+ private Task SendMessage(string name, string messageId, CancellationToken cancellationToken)
{
- return SendMessage(name, new Dictionary<string, string>(), cancellationToken);
+ return SendMessage(name, messageId, new Dictionary<string, string>(), cancellationToken);
}
- private async Task SendMessage(string name,
- Dictionary<string, string> args,
- CancellationToken cancellationToken)
+ private Task SendMessage(string name, string messageId, Dictionary<string, string> args, CancellationToken cancellationToken)
{
+ args["messageId"] = messageId;
var url = PostUrl + "/" + name + ToQueryString(args);
- using ((await _httpClient.Post(new HttpRequestOptions
+ return SendRequest(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = false
-
- }).ConfigureAwait(false)))
- {
-
- }
- }
-
- public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
+ });
}
- public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
+ private Task SendPlayCommand(PlayRequest command, string messageId, CancellationToken cancellationToken)
{
var dict = new Dictionary<string, string>();
- dict["ItemIds"] = string.Join(",", command.ItemIds);
+ dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N")).ToArray());
if (command.StartPositionTicks.HasValue)
{
@@ -121,15 +97,15 @@ namespace Emby.Server.Implementations.Session
{
dict["StartIndex"] = command.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
}
- if (!string.IsNullOrWhiteSpace(command.MediaSourceId))
+ if (!string.IsNullOrEmpty(command.MediaSourceId))
{
dict["MediaSourceId"] = command.MediaSourceId;
}
- return SendMessage(command.PlayCommand.ToString(), dict, cancellationToken);
+ return SendMessage(command.PlayCommand.ToString(), messageId, dict, cancellationToken);
}
- public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
+ private Task SendPlaystateCommand(PlaystateRequest command, string messageId, CancellationToken cancellationToken)
{
var args = new Dictionary<string, string>();
@@ -143,60 +119,74 @@ namespace Emby.Server.Implementations.Session
args["SeekPositionTicks"] = command.SeekPositionTicks.Value.ToString(CultureInfo.InvariantCulture);
}
- return SendMessage(command.Command.ToString(), args, cancellationToken);
+ return SendMessage(command.Command.ToString(), messageId, args, cancellationToken);
}
- public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
+ private string[] _supportedMessages = new string[] { };
+ public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
{
- return SendMessage("LibraryChanged", info, cancellationToken);
- }
+ if (!IsSessionActive)
+ {
+ return Task.CompletedTask;
+ }
- public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
- {
- return SendMessage("RestartRequired", cancellationToken);
- }
+ if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlayCommand(data as PlayRequest, messageId, cancellationToken);
+ }
+ if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlaystateCommand(data as PlaystateRequest, messageId, cancellationToken);
+ }
+ if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
+ {
+ var command = data as GeneralCommand;
+ return SendMessage(command.Name, messageId, command.Arguments, cancellationToken);
+ }
- public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
+ if (!_supportedMessages.Contains(name, StringComparer.OrdinalIgnoreCase))
+ {
+ return Task.CompletedTask;
+ }
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- return SendMessage("ServerShuttingDown", cancellationToken);
- }
+ var url = PostUrl + "/" + name;
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- return SendMessage("ServerRestarting", cancellationToken);
- }
+ url += "?messageId=" + messageId;
- public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
- {
- return SendMessage(command.Name, command.Arguments, cancellationToken);
+ var options = new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = cancellationToken,
+ BufferContent = false
+ };
+
+ if (data != null)
+ {
+ if (typeof(T) == typeof(string))
+ {
+ var str = data as String;
+ if (!string.IsNullOrEmpty(str))
+ {
+ options.RequestContent = str;
+ options.RequestContentType = "application/json";
+ }
+ }
+ else
+ {
+ options.RequestContent = _json.SerializeToString(data);
+ options.RequestContentType = "application/json";
+ }
+ }
+
+ return SendRequest(options);
}
- public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
+ private async Task SendRequest(HttpRequestOptions options)
{
- return Task.FromResult(true);
- //var url = PostUrl + "/" + name;
-
- //var options = new HttpRequestOptions
- //{
- // Url = url,
- // CancellationToken = cancellationToken,
- // BufferContent = false
- //};
-
- //options.RequestContent = _json.SerializeToString(data);
- //options.RequestContentType = "application/json";
-
- //return _httpClient.Post(new HttpRequestOptions
- //{
- // Url = url,
- // CancellationToken = cancellationToken,
- // BufferContent = false
- //});
+ using (var response = await _httpClient.Post(options).ConfigureAwait(false))
+ {
+
+ }
}
private string ToQueryString(Dictionary<string, string> nvc)
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 6b70f2cda..9db4f4423 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -30,13 +30,14 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Authentication;
namespace Emby.Server.Implementations.Session
{
/// <summary>
/// Class SessionManager
/// </summary>
- public class SessionManager : ISessionManager
+ public class SessionManager : ISessionManager, IDisposable
{
/// <summary>
/// The _user data repository
@@ -71,7 +72,7 @@ namespace Emby.Server.Implementations.Session
public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
- public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded;
+ public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
/// <summary>
/// Occurs when [playback start].
@@ -91,10 +92,6 @@ namespace Emby.Server.Implementations.Session
public event EventHandler<SessionEventArgs> SessionEnded;
public event EventHandler<SessionEventArgs> SessionActivity;
- private IEnumerable<ISessionControllerFactory> _sessionFactories = new List<ISessionControllerFactory>();
-
- private readonly SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
-
public SessionManager(IUserDataManager userDataManager, ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient, IAuthenticationRepository authRepo, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, ITimerFactory timerFactory)
{
_userDataManager = userDataManager;
@@ -111,28 +108,41 @@ namespace Emby.Server.Implementations.Session
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
_timerFactory = timerFactory;
-
_deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
}
- void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<DeviceInfo> e)
+ private void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
{
foreach (var session in Sessions)
{
- if (string.Equals(session.DeviceId, e.Argument.Id))
+ if (string.Equals(session.DeviceId, e.Argument.Item1))
{
- session.DeviceName = e.Argument.Name;
+ if (!string.IsNullOrWhiteSpace(e.Argument.Item2.CustomName))
+ {
+ session.HasCustomDeviceName = true;
+ session.DeviceName = e.Argument.Item2.CustomName;
+ }
+ else
+ {
+ session.HasCustomDeviceName = false;
+ }
}
}
}
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="sessionFactories">The session factories.</param>
- public void AddParts(IEnumerable<ISessionControllerFactory> sessionFactories)
+ private bool _disposed;
+ public void Dispose()
+ {
+ _disposed = true;
+ _deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated;
+ }
+
+ public void CheckDisposed()
{
- _sessionFactories = sessionFactories.ToList();
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
}
/// <summary>
@@ -146,58 +156,44 @@ namespace Emby.Server.Implementations.Session
private void OnSessionStarted(SessionInfo info)
{
- EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs
- {
- SessionInfo = info
-
- }, _logger);
-
- if (!string.IsNullOrWhiteSpace(info.DeviceId))
+ if (!string.IsNullOrEmpty(info.DeviceId))
{
var capabilities = GetSavedCapabilities(info.DeviceId);
if (capabilities != null)
{
- info.AppIconUrl = capabilities.IconUrl;
ReportCapabilities(info, capabilities, false);
}
}
- }
- private async void OnSessionEnded(SessionInfo info)
- {
- try
- {
- await SendSessionEndedNotification(info, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
+ EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs
{
- _logger.ErrorException("Error in SendSessionEndedNotification", ex);
- }
+ SessionInfo = info
+ }, _logger);
+ }
+
+ private void OnSessionEnded(SessionInfo info)
+ {
EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs
{
SessionInfo = info
}, _logger);
- var disposable = info.SessionController as IDisposable;
+ info.Dispose();
+ }
- if (disposable != null)
- {
- _logger.Debug("Disposing session controller {0}", disposable.GetType().Name);
+ public void UpdateDeviceName(string sessionId, string deviceName)
+ {
+ var session = GetSession(sessionId);
- try
- {
- disposable.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing session controller", ex);
- }
- }
+ var key = GetSessionKey(session.Client, session.DeviceId);
- info.Dispose();
+ if (session != null)
+ {
+ session.DeviceName = deviceName;
+ }
}
/// <summary>
@@ -212,13 +208,15 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">user</exception>
/// <exception cref="System.UnauthorizedAccessException"></exception>
- public async Task<SessionInfo> LogSessionActivity(string appName,
+ public SessionInfo LogSessionActivity(string appName,
string appVersion,
string deviceId,
string deviceName,
string remoteEndPoint,
User user)
{
+ CheckDisposed();
+
if (string.IsNullOrEmpty(appName))
{
throw new ArgumentNullException("appName");
@@ -231,13 +229,9 @@ namespace Emby.Server.Implementations.Session
{
throw new ArgumentNullException("deviceId");
}
- if (string.IsNullOrEmpty(deviceName))
- {
- throw new ArgumentNullException("deviceName");
- }
var activityDate = DateTime.UtcNow;
- var session = await GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false);
+ var session = GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
var lastActivityDate = session.LastActivityDate;
session.LastActivityDate = activityDate;
@@ -268,40 +262,39 @@ namespace Emby.Server.Implementations.Session
}, _logger);
}
- var controller = session.SessionController;
- if (controller != null)
- {
- controller.OnActivity();
- }
-
return session;
}
- public async void ReportSessionEnded(string sessionId)
+ public void CloseIfNeeded(SessionInfo session)
{
- await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- try
+ if (!session.SessionControllers.Any(i => i.IsSessionActive))
{
- var session = GetSession(sessionId, false);
+ var key = GetSessionKey(session.Client, session.DeviceId);
- if (session != null)
- {
- var key = GetSessionKey(session.Client, session.DeviceId);
-
- SessionInfo removed;
- _activeConnections.TryRemove(key, out removed);
+ SessionInfo removed;
+ _activeConnections.TryRemove(key, out removed);
- OnSessionEnded(session);
- }
+ OnSessionEnded(session);
}
- finally
+ }
+
+ public void ReportSessionEnded(string sessionId)
+ {
+ CheckDisposed();
+ var session = GetSession(sessionId, false);
+
+ if (session != null)
{
- _sessionLock.Release();
+ var key = GetSessionKey(session.Client, session.DeviceId);
+
+ SessionInfo removed;
+ _activeConnections.TryRemove(key, out removed);
+
+ OnSessionEnded(session);
}
}
- private Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId)
+ private Task<MediaSourceInfo> GetMediaSource(BaseItem item, string mediaSourceId, string liveStreamId)
{
return _mediaSourceManager.GetMediaSource(item, mediaSourceId, liveStreamId, false, CancellationToken.None);
}
@@ -311,16 +304,16 @@ namespace Emby.Server.Implementations.Session
/// </summary>
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
{
- if (string.IsNullOrWhiteSpace(info.MediaSourceId))
+ if (string.IsNullOrEmpty(info.MediaSourceId))
{
- info.MediaSourceId = info.ItemId;
+ info.MediaSourceId = info.ItemId.ToString("N");
}
- if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
+ if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
{
var current = session.NowPlayingItem;
- if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase))
+ if (current == null || !info.ItemId.Equals(current.Id))
{
var runtimeTicks = libraryItem.RunTimeTicks;
@@ -328,7 +321,7 @@ namespace Emby.Server.Implementations.Session
var hasMediaSources = libraryItem as IHasMediaSources;
if (hasMediaSources != null)
{
- mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
+ mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
if (mediaSource != null)
{
@@ -364,6 +357,14 @@ namespace Emby.Server.Implementations.Session
session.PlayState.SubtitleStreamIndex = info.SubtitleStreamIndex;
session.PlayState.PlayMethod = info.PlayMethod;
session.PlayState.RepeatMode = info.RepeatMode;
+ session.PlaylistItemId = info.PlaylistItemId;
+
+ var nowPlayingQueue = info.NowPlayingQueue;
+
+ if (nowPlayingQueue != null)
+ {
+ session.NowPlayingQueue = nowPlayingQueue;
+ }
}
/// <summary>
@@ -397,99 +398,89 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- private async Task<SessionInfo> GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
{
- if (string.IsNullOrWhiteSpace(deviceId))
+ CheckDisposed();
+
+ if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException("deviceId");
}
var key = GetSessionKey(appName, deviceId);
- await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
+ CheckDisposed();
- var userId = user == null ? (Guid?)null : user.Id;
- var username = user == null ? null : user.Name;
-
- try
+ SessionInfo sessionInfo = _activeConnections.GetOrAdd(key, k =>
{
- SessionInfo sessionInfo;
- DeviceInfo device = null;
-
- if (!_activeConnections.TryGetValue(key, out sessionInfo))
- {
- sessionInfo = new SessionInfo(this, _logger)
- {
- Client = appName,
- DeviceId = deviceId,
- ApplicationVersion = appVersion,
- Id = key.GetMD5().ToString("N")
- };
-
- sessionInfo.DeviceName = deviceName;
- sessionInfo.UserId = userId;
- sessionInfo.UserName = username;
- sessionInfo.RemoteEndPoint = remoteEndPoint;
+ return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
+ });
- OnSessionStarted(sessionInfo);
+ sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
+ sessionInfo.UserName = user == null ? null : user.Name;
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.RemoteEndPoint = remoteEndPoint;
+ sessionInfo.Client = appName;
- _activeConnections.TryAdd(key, sessionInfo);
+ if (!sessionInfo.HasCustomDeviceName || string.IsNullOrEmpty(sessionInfo.DeviceName))
+ {
+ sessionInfo.DeviceName = deviceName;
+ }
- if (!string.IsNullOrEmpty(deviceId))
- {
- var userIdString = userId.HasValue ? userId.Value.ToString("N") : null;
- device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString);
- }
- }
+ sessionInfo.ApplicationVersion = appVersion;
- device = device ?? _deviceManager.GetDevice(deviceId);
+ if (user == null)
+ {
+ sessionInfo.AdditionalUsers = new SessionUserInfo[] { };
+ }
- if (device == null)
- {
- var userIdString = userId.HasValue ? userId.Value.ToString("N") : null;
- device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString);
- }
+ return sessionInfo;
+ }
- if (device != null)
- {
- if (!string.IsNullOrEmpty(device.CustomName))
- {
- deviceName = device.CustomName;
- }
- }
+ private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ {
+ var sessionInfo = new SessionInfo(this, _logger)
+ {
+ Client = appName,
+ DeviceId = deviceId,
+ ApplicationVersion = appVersion,
+ Id = key.GetMD5().ToString("N"),
+ ServerId = _appHost.SystemId
+ };
- sessionInfo.DeviceName = deviceName;
- sessionInfo.UserId = userId;
- sessionInfo.UserName = username;
- sessionInfo.RemoteEndPoint = remoteEndPoint;
- sessionInfo.ApplicationVersion = appVersion;
+ var username = user == null ? null : user.Name;
- if (!userId.HasValue)
- {
- sessionInfo.AdditionalUsers = new SessionUserInfo[] { };
- }
+ sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
+ sessionInfo.UserName = username;
+ sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.RemoteEndPoint = remoteEndPoint;
- if (sessionInfo.SessionController == null)
- {
- sessionInfo.SessionController = _sessionFactories
- .Select(i => i.GetSessionController(sessionInfo))
- .FirstOrDefault(i => i != null);
- }
+ if (string.IsNullOrEmpty(deviceName))
+ {
+ deviceName = "Network Device";
+ }
- return sessionInfo;
+ var deviceOptions = _deviceManager.GetDeviceOptions(deviceId);
+ if (string.IsNullOrEmpty(deviceOptions.CustomName))
+ {
+ sessionInfo.DeviceName = deviceName;
}
- finally
+ else
{
- _sessionLock.Release();
+ sessionInfo.DeviceName = deviceOptions.CustomName;
+ sessionInfo.HasCustomDeviceName = true;
}
+
+ OnSessionStarted(sessionInfo);
+ return sessionInfo;
}
private List<User> GetUsers(SessionInfo session)
{
var users = new List<User>();
- if (session.UserId.HasValue)
+ if (!session.UserId.Equals(Guid.Empty))
{
- var user = _userManager.GetUserById(session.UserId.Value);
+ var user = _userManager.GetUserById(session.UserId);
if (user == null)
{
@@ -498,11 +489,9 @@ namespace Emby.Server.Implementations.Session
users.Add(user);
- var additionalUsers = session.AdditionalUsers
+ users.AddRange(session.AdditionalUsers
.Select(i => _userManager.GetUserById(i.UserId))
- .Where(i => i != null);
-
- users.AddRange(additionalUsers);
+ .Where(i => i != null));
}
return users;
@@ -546,7 +535,7 @@ namespace Emby.Server.Implementations.Session
await OnPlaybackStopped(new PlaybackStopInfo
{
Item = session.NowPlayingItem,
- ItemId = session.NowPlayingItem == null ? null : session.NowPlayingItem.Id,
+ ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id,
SessionId = session.Id,
MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId,
PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks
@@ -568,12 +557,10 @@ namespace Emby.Server.Implementations.Session
}
}
- private BaseItem GetNowPlayingItem(SessionInfo session, string itemId)
+ private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId)
{
- var idGuid = new Guid(itemId);
-
var item = session.FullNowPlayingItem;
- if (item != null && item.Id == idGuid)
+ if (item != null && item.Id.Equals(itemId))
{
return item;
}
@@ -593,6 +580,8 @@ namespace Emby.Server.Implementations.Session
/// <exception cref="System.ArgumentNullException">info</exception>
public async Task OnPlaybackStart(PlaybackStartInfo info)
{
+ CheckDisposed();
+
if (info == null)
{
throw new ArgumentNullException("info");
@@ -600,7 +589,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
+ var libraryItem = info.ItemId.Equals(Guid.Empty)
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -619,7 +608,7 @@ namespace Emby.Server.Implementations.Session
{
foreach (var user in users)
{
- OnPlaybackStart(user.Id, libraryItem);
+ OnPlaybackStart(user, libraryItem);
}
}
@@ -633,12 +622,11 @@ namespace Emby.Server.Implementations.Session
MediaInfo = info.Item,
DeviceName = session.DeviceName,
ClientName = session.Client,
- DeviceId = session.DeviceId
+ DeviceId = session.DeviceId,
+ Session = session
}, _logger);
- await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false);
-
StartIdleCheckTimer();
}
@@ -647,9 +635,9 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <param name="item">The item.</param>
- private void OnPlaybackStart(Guid userId, IHasUserData item)
+ private void OnPlaybackStart(User user, BaseItem item)
{
- var data = _userDataManager.GetUserData(userId, item);
+ var data = _userDataManager.GetUserData(user, item);
data.PlayCount++;
data.LastPlayedDate = DateTime.UtcNow;
@@ -666,7 +654,7 @@ namespace Emby.Server.Implementations.Session
data.Played = false;
}
- _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
+ _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
}
public Task OnPlaybackProgress(PlaybackProgressInfo info)
@@ -679,6 +667,8 @@ namespace Emby.Server.Implementations.Session
/// </summary>
public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
{
+ CheckDisposed();
+
if (info == null)
{
throw new ArgumentNullException("info");
@@ -686,7 +676,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
+ var libraryItem = info.ItemId.Equals(Guid.Empty)
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -694,7 +684,8 @@ namespace Emby.Server.Implementations.Session
var users = GetUsers(session);
- if (libraryItem != null)
+ // only update saved user data on actual check-ins, not automated ones
+ if (libraryItem != null && !isAutomated)
{
foreach (var user in users)
{
@@ -714,7 +705,8 @@ namespace Emby.Server.Implementations.Session
DeviceId = session.DeviceId,
IsPaused = info.IsPaused,
PlaySessionId = info.PlaySessionId,
- IsAutomated = isAutomated
+ IsAutomated = isAutomated,
+ Session = session
}, _logger);
@@ -728,39 +720,70 @@ namespace Emby.Server.Implementations.Session
private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
{
- var data = _userDataManager.GetUserData(user.Id, item);
+ var data = _userDataManager.GetUserData(user, item);
var positionTicks = info.PositionTicks;
+ var changed = false;
+
if (positionTicks.HasValue)
{
_userDataManager.UpdatePlayState(item, data, positionTicks.Value);
+ changed = true;
+ }
- UpdatePlaybackSettings(user, info, data);
+ var tracksChanged = UpdatePlaybackSettings(user, info, data);
+ if (!tracksChanged)
+ {
+ changed = true;
+ }
- _userDataManager.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
+ if (changed)
+ {
+ _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
}
+
}
- private void UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
+ private bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
{
+ var changed = false;
+
if (user.Configuration.RememberAudioSelections)
{
- data.AudioStreamIndex = info.AudioStreamIndex;
+ if (data.AudioStreamIndex != info.AudioStreamIndex)
+ {
+ data.AudioStreamIndex = info.AudioStreamIndex;
+ changed = true;
+ }
}
else
{
- data.AudioStreamIndex = null;
+ if (data.AudioStreamIndex.HasValue)
+ {
+ data.AudioStreamIndex = null;
+ changed = true;
+ }
}
if (user.Configuration.RememberSubtitleSelections)
{
- data.SubtitleStreamIndex = info.SubtitleStreamIndex;
+ if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
+ {
+ data.SubtitleStreamIndex = info.SubtitleStreamIndex;
+ changed = true;
+ }
}
else
{
- data.SubtitleStreamIndex = null;
+ if (data.SubtitleStreamIndex.HasValue)
+ {
+ data.SubtitleStreamIndex = null;
+ changed = true;
+ }
}
+
+ return changed;
}
/// <summary>
@@ -772,6 +795,8 @@ namespace Emby.Server.Implementations.Session
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
public async Task OnPlaybackStopped(PlaybackStopInfo info)
{
+ CheckDisposed();
+
if (info == null)
{
throw new ArgumentNullException("info");
@@ -786,28 +811,28 @@ namespace Emby.Server.Implementations.Session
session.StopAutomaticProgress();
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
+ var libraryItem = info.ItemId.Equals(Guid.Empty)
? null
: GetNowPlayingItem(session, info.ItemId);
// Normalize
- if (string.IsNullOrWhiteSpace(info.MediaSourceId))
+ if (string.IsNullOrEmpty(info.MediaSourceId))
{
- info.MediaSourceId = info.ItemId;
+ info.MediaSourceId = info.ItemId.ToString("N");
}
- if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
+ if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
{
var current = session.NowPlayingItem;
- if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase))
+ if (current == null || !info.ItemId.Equals(current.Id))
{
MediaSourceInfo mediaSource = null;
var hasMediaSources = libraryItem as IHasMediaSources;
if (hasMediaSources != null)
{
- mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
+ mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
}
info.Item = GetItemInfo(libraryItem, mediaSource);
@@ -829,6 +854,13 @@ namespace Emby.Server.Implementations.Session
msString);
}
+ if (info.NowPlayingQueue != null)
+ {
+ session.NowPlayingQueue = info.NowPlayingQueue;
+ }
+
+ session.PlaylistItemId = info.PlaylistItemId;
+
RemoveNowPlayingItem(session);
var users = GetUsers(session);
@@ -838,11 +870,11 @@ namespace Emby.Server.Implementations.Session
{
foreach (var user in users)
{
- playedToCompletion = OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed);
+ playedToCompletion = OnPlaybackStopped(user, libraryItem, info.PositionTicks, info.Failed);
}
}
- if (!string.IsNullOrWhiteSpace(info.LiveStreamId))
+ if (!string.IsNullOrEmpty(info.LiveStreamId))
{
try
{
@@ -864,20 +896,19 @@ namespace Emby.Server.Implementations.Session
MediaInfo = info.Item,
DeviceName = session.DeviceName,
ClientName = session.Client,
- DeviceId = session.DeviceId
+ DeviceId = session.DeviceId,
+ Session = session
}, _logger);
-
- await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false);
}
- private bool OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed)
+ private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
{
bool playedToCompletion = false;
if (!playbackFailed)
{
- var data = _userDataManager.GetUserData(userId, item);
+ var data = _userDataManager.GetUserData(user, item);
if (positionTicks.HasValue)
{
@@ -892,7 +923,7 @@ namespace Emby.Server.Implementations.Session
playedToCompletion = true;
}
- _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
+ _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
}
return playedToCompletion;
@@ -932,6 +963,8 @@ namespace Emby.Server.Implementations.Session
public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
{
+ CheckDisposed();
+
var generalCommand = new GeneralCommand
{
Name = GeneralCommandType.DisplayMessage.ToString()
@@ -950,22 +983,37 @@ namespace Emby.Server.Implementations.Session
public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken)
{
+ CheckDisposed();
+
var session = GetSessionToRemoteControl(sessionId);
- if (!string.IsNullOrWhiteSpace(controllingSessionId))
+ if (!string.IsNullOrEmpty(controllingSessionId))
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
}
- return session.SessionController.SendGeneralCommand(command, cancellationToken);
+ return SendMessageToSession(session, "GeneralCommand", command, cancellationToken);
+ }
+
+ private async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
+ {
+ var controllers = session.SessionControllers.ToArray();
+ var messageId = Guid.NewGuid().ToString("N");
+
+ foreach (var controller in controllers)
+ {
+ await controller.SendMessage(name, messageId, data, controllers, cancellationToken).ConfigureAwait(false);
+ }
}
public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
{
+ CheckDisposed();
+
var session = GetSessionToRemoteControl(sessionId);
- var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null;
+ var user = !session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(session.UserId) : null;
List<BaseItem> items;
@@ -994,7 +1042,7 @@ namespace Emby.Server.Implementations.Session
command.PlayCommand = PlayCommand.PlayNow;
}
- command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray(items.Count);
+ command.ItemIds = items.Select(i => i.Id).ToArray(items.Count);
if (user != null)
{
@@ -1004,11 +1052,6 @@ namespace Emby.Server.Implementations.Session
}
}
- if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id));
- }
-
if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay)
{
var episode = _libraryManager.GetItemById(command.ItemIds[0]) as Episode;
@@ -1027,26 +1070,26 @@ namespace Emby.Server.Implementations.Session
if (episodes.Count > 0)
{
- command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray(episodes.Count);
+ command.ItemIds = episodes.Select(i => i.Id).ToArray(episodes.Count);
}
}
}
}
- if (!string.IsNullOrWhiteSpace(controllingSessionId))
+ if (!string.IsNullOrEmpty(controllingSessionId))
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
+ if (!controllingSession.UserId.Equals(Guid.Empty))
{
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
+ command.ControllingUserId = controllingSession.UserId;
}
}
- await session.SessionController.SendPlayCommand(command, cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
}
- private List<BaseItem> TranslateItemForPlayback(string id, User user)
+ private IList<BaseItem> TranslateItemForPlayback(Guid id, User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1060,7 +1103,7 @@ namespace Emby.Server.Implementations.Session
if (byName != null)
{
- var items = byName.GetTaggedItems(new InternalItemsQuery(user)
+ return byName.GetTaggedItems(new InternalItemsQuery(user)
{
IsFolder = false,
Recursive = true,
@@ -1072,19 +1115,16 @@ namespace Emby.Server.Implementations.Session
ItemFields.SortName
}
},
- IsVirtualItem = false
+ IsVirtualItem = false,
+ OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
});
-
- return FilterToSingleMediaType(items)
- .OrderBy(i => i.SortName)
- .ToList();
}
if (item.IsFolder)
{
var folder = (Folder)item;
- var itemsResult = folder.GetItemList(new InternalItemsQuery(user)
+ return folder.GetItemList(new InternalItemsQuery(user)
{
Recursive = true,
IsFolder = false,
@@ -1096,28 +1136,16 @@ namespace Emby.Server.Implementations.Session
ItemFields.SortName
}
},
- IsVirtualItem = false
+ IsVirtualItem = false,
+ OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
});
-
- return FilterToSingleMediaType(itemsResult)
- .OrderBy(i => i.SortName)
- .ToList();
}
return new List<BaseItem> { item };
}
- private IEnumerable<BaseItem> FilterToSingleMediaType(IEnumerable<BaseItem> items)
- {
- return items
- .Where(i => !string.IsNullOrWhiteSpace(i.MediaType))
- .ToLookup(i => i.MediaType, StringComparer.OrdinalIgnoreCase)
- .OrderByDescending(i => i.Count())
- .FirstOrDefault();
- }
-
- private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user)
+ private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1146,19 +1174,21 @@ namespace Emby.Server.Implementations.Session
public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken)
{
+ CheckDisposed();
+
var session = GetSessionToRemoteControl(sessionId);
- if (!string.IsNullOrWhiteSpace(controllingSessionId))
+ if (!string.IsNullOrEmpty(controllingSessionId))
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
+ if (!controllingSession.UserId.Equals(Guid.Empty))
{
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
+ command.ControllingUserId = controllingSession.UserId.ToString("N");
}
}
- return session.SessionController.SendPlaystateCommand(command, cancellationToken);
+ return SendMessageToSession(session, "Playstate", command, cancellationToken);
}
private void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
@@ -1180,20 +1210,22 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
public async Task SendRestartRequiredNotification(CancellationToken cancellationToken)
{
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
+ CheckDisposed();
+
+ var sessions = Sessions.ToList();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
- await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, "RestartRequired", string.Empty, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error in SendRestartRequiredNotification.", ex);
}
- }, cancellationToken));
+ }, cancellationToken)).ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
}
@@ -1205,20 +1237,22 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
{
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
+ CheckDisposed();
+
+ var sessions = Sessions.ToList();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
- await session.SessionController.SendServerShutdownNotification(cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, "ServerShuttingDown", string.Empty, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error in SendServerShutdownNotification.", ex);
}
- }, cancellationToken));
+ }, cancellationToken)).ToArray();
return Task.WhenAll(tasks);
}
@@ -1230,85 +1264,24 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
public Task SendServerRestartNotification(CancellationToken cancellationToken)
{
+ CheckDisposed();
+
_logger.Debug("Beginning SendServerRestartNotification");
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
+ var sessions = Sessions.ToList();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
- await session.SessionController.SendServerRestartNotification(cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, "ServerRestarting", string.Empty, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error in SendServerRestartNotification.", ex);
}
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendSessionEndedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendSessionEndedNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendSessionEndedNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendPlaybackStartNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendPlaybackStartNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendPlaybackStartNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendPlaybackStoppedNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendPlaybackStoppedNotification.", ex);
- }
-
- }, cancellationToken));
+ }, cancellationToken)).ToArray();
return Task.WhenAll(tasks);
}
@@ -1320,16 +1293,18 @@ namespace Emby.Server.Implementations.Session
/// <param name="userId">The user identifier.</param>
/// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception>
/// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception>
- public void AddAdditionalUser(string sessionId, string userId)
+ public void AddAdditionalUser(string sessionId, Guid userId)
{
+ CheckDisposed();
+
var session = GetSession(sessionId);
- if (session.UserId.HasValue && session.UserId.Value == new Guid(userId))
+ if (session.UserId.Equals(userId))
{
throw new ArgumentException("The requested user is already the primary user of the session.");
}
- if (session.AdditionalUsers.All(i => new Guid(i.UserId) != new Guid(userId)))
+ if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId)))
{
var user = _userManager.GetUserById(userId);
@@ -1352,16 +1327,18 @@ namespace Emby.Server.Implementations.Session
/// <param name="userId">The user identifier.</param>
/// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception>
/// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception>
- public void RemoveAdditionalUser(string sessionId, string userId)
+ public void RemoveAdditionalUser(string sessionId, Guid userId)
{
+ CheckDisposed();
+
var session = GetSession(sessionId);
- if (session.UserId.HasValue && session.UserId.Value == new Guid(userId))
+ if (session.UserId.Equals(userId))
{
throw new ArgumentException("The requested user is already the primary user of the session.");
}
- var user = session.AdditionalUsers.FirstOrDefault(i => new Guid(i.UserId) == new Guid(userId));
+ var user = session.AdditionalUsers.FirstOrDefault(i => i.UserId.Equals(userId));
if (user != null)
{
@@ -1389,12 +1366,13 @@ namespace Emby.Server.Implementations.Session
private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
{
+ CheckDisposed();
+
User user = null;
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
- var idGuid = new Guid(request.UserId);
user = _userManager.Users
- .FirstOrDefault(i => i.Id == idGuid);
+ .FirstOrDefault(i => i.Id == request.UserId);
}
if (user == null)
@@ -1405,14 +1383,10 @@ namespace Emby.Server.Implementations.Session
if (user != null)
{
- if (!user.IsParentalScheduleAllowed())
- {
- throw new SecurityException("User is not allowed access at this time.");
- }
-
- if (!string.IsNullOrWhiteSpace(request.DeviceId))
+ // TODO: Move this to userManager?
+ if (!string.IsNullOrEmpty(request.DeviceId))
{
- if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId))
+ if (!_deviceManager.CanAccessDevice(user, request.DeviceId))
{
throw new SecurityException("User is not allowed access from this device.");
}
@@ -1421,7 +1395,7 @@ namespace Emby.Server.Implementations.Session
if (enforcePassword)
{
- var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint, true).ConfigureAwait(false);
+ var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.RemoteEndPoint, true).ConfigureAwait(false);
if (result == null)
{
@@ -1433,72 +1407,95 @@ namespace Emby.Server.Implementations.Session
user = result;
}
- var token = GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName);
+ var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
- EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
-
- var session = await LogSessionActivity(request.App,
+ var session = LogSessionActivity(request.App,
request.AppVersion,
request.DeviceId,
request.DeviceName,
request.RemoteEndPoint,
- user)
- .ConfigureAwait(false);
+ user);
- return new AuthenticationResult
+ var returnResult = new AuthenticationResult
{
User = _userManager.GetUserDto(user, request.RemoteEndPoint),
- SessionInfo = GetSessionInfoDto(session),
+ SessionInfo = session,
AccessToken = token,
ServerId = _appHost.SystemId
};
- }
+ EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationResult>(returnResult), _logger);
+
+ return returnResult;
+ }
- private string GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName)
+ private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
{
var existing = _authRepo.Get(new AuthenticationInfoQuery
{
DeviceId = deviceId,
- IsActive = true,
- UserId = userId,
+ UserId = user.Id,
Limit = 1
- });
- if (existing.Items.Length > 0)
+ }).Items.FirstOrDefault();
+
+ var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ DeviceId = deviceId
+
+ }).Items;
+
+ foreach (var auth in allExistingForDevice)
+ {
+ if (existing == null || !string.Equals(auth.AccessToken, existing.AccessToken, StringComparison.Ordinal))
+ {
+ try
+ {
+ Logout(auth);
+ }
+ catch
+ {
+
+ }
+ }
+ }
+
+ if (existing != null)
{
- var token = existing.Items[0].AccessToken;
- _logger.Info("Reissuing access token: " + token);
- return token;
+ _logger.Info("Reissuing access token: " + existing.AccessToken);
+ return existing.AccessToken;
}
+ var now = DateTime.UtcNow;
+
var newToken = new AuthenticationInfo
{
AppName = app,
AppVersion = appVersion,
- DateCreated = DateTime.UtcNow,
+ DateCreated = now,
+ DateLastActivity = now,
DeviceId = deviceId,
DeviceName = deviceName,
- UserId = userId,
- IsActive = true,
- AccessToken = Guid.NewGuid().ToString("N")
+ UserId = user.Id,
+ AccessToken = Guid.NewGuid().ToString("N"),
+ UserName = user.Name
};
- _logger.Info("Creating new access token for user {0}", userId);
- _authRepo.Create(newToken, CancellationToken.None);
+ _logger.Info("Creating new access token for user {0}", user.Id);
+ _authRepo.Create(newToken);
return newToken.AccessToken;
}
public void Logout(string accessToken)
{
- if (string.IsNullOrWhiteSpace(accessToken))
+ CheckDisposed();
+
+ if (string.IsNullOrEmpty(accessToken))
{
throw new ArgumentNullException("accessToken");
}
- _logger.Info("Logging out access token {0}", accessToken);
-
var existing = _authRepo.Get(new AuthenticationInfoQuery
{
Limit = 1,
@@ -1508,33 +1505,41 @@ namespace Emby.Server.Implementations.Session
if (existing != null)
{
- existing.IsActive = false;
+ Logout(existing);
+ }
+ }
- _authRepo.Update(existing, CancellationToken.None);
+ public void Logout(AuthenticationInfo existing)
+ {
+ CheckDisposed();
- var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
- .ToList();
+ _logger.Info("Logging out access token {0}", existing.AccessToken);
+
+ _authRepo.Delete(existing);
- foreach (var session in sessions)
+ var sessions = Sessions
+ .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ foreach (var session in sessions)
+ {
+ try
{
- try
- {
- ReportSessionEnded(session.Id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reporting session ended", ex);
- }
+ ReportSessionEnded(session.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error reporting session ended", ex);
}
}
}
- public void RevokeUserTokens(string userId, string currentAccessToken)
+ public void RevokeUserTokens(Guid userId, string currentAccessToken)
{
+ CheckDisposed();
+
var existing = _authRepo.Get(new AuthenticationInfoQuery
{
- IsActive = true,
UserId = userId
});
@@ -1542,7 +1547,7 @@ namespace Emby.Server.Implementations.Session
{
if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase))
{
- Logout(info.AccessToken);
+ Logout(info);
}
}
}
@@ -1559,6 +1564,8 @@ namespace Emby.Server.Implementations.Session
/// <param name="capabilities">The capabilities.</param>
public void ReportCapabilities(string sessionId, ClientCapabilities capabilities)
{
+ CheckDisposed();
+
var session = GetSession(sessionId);
ReportCapabilities(session, capabilities, true);
@@ -1570,24 +1577,22 @@ namespace Emby.Server.Implementations.Session
{
session.Capabilities = capabilities;
- if (!string.IsNullOrWhiteSpace(capabilities.MessageCallbackUrl))
+ if (!string.IsNullOrEmpty(capabilities.PushToken))
{
- var controller = session.SessionController as HttpSessionController;
-
- if (controller == null)
+ if (string.Equals(capabilities.PushTokenType, "firebase", StringComparison.OrdinalIgnoreCase) && FirebaseSessionController.IsSupported(_appHost))
{
- session.SessionController = new HttpSessionController(_httpClient, _jsonSerializer, session, capabilities.MessageCallbackUrl, this);
+ EnsureFirebaseController(session, capabilities.PushToken);
}
}
- EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs
+ if (saveCapabilities)
{
- SessionInfo = session
+ EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs
+ {
+ SessionInfo = session
- }, _logger);
+ }, _logger);
- if (saveCapabilities)
- {
try
{
SaveCapabilities(session.DeviceId, capabilities);
@@ -1599,6 +1604,11 @@ namespace Emby.Server.Implementations.Session
}
}
+ private void EnsureFirebaseController(SessionInfo session, string token)
+ {
+ session.EnsureController<FirebaseSessionController>(s => new FirebaseSessionController(_httpClient, _appHost, _jsonSerializer, s, token, this));
+ }
+
private ClientCapabilities GetSavedCapabilities(string deviceId)
{
return _deviceManager.GetCapabilities(deviceId);
@@ -1609,45 +1619,6 @@ namespace Emby.Server.Implementations.Session
_deviceManager.SaveCapabilities(deviceId, capabilities);
}
- public SessionInfoDto GetSessionInfoDto(SessionInfo session)
- {
- var dto = new SessionInfoDto
- {
- Client = session.Client,
- DeviceId = session.DeviceId,
- DeviceName = session.DeviceName,
- Id = session.Id,
- LastActivityDate = session.LastActivityDate,
- NowViewingItem = session.NowViewingItem,
- ApplicationVersion = session.ApplicationVersion,
- PlayableMediaTypes = session.PlayableMediaTypes,
- AdditionalUsers = session.AdditionalUsers,
- SupportedCommands = session.SupportedCommands,
- UserName = session.UserName,
- NowPlayingItem = session.NowPlayingItem,
- SupportsRemoteControl = session.SupportsMediaControl,
- PlayState = session.PlayState,
- AppIconUrl = session.AppIconUrl,
- TranscodingInfo = session.NowPlayingItem == null ? null : session.TranscodingInfo
- };
-
- dto.ServerId = _appHost.SystemId;
-
- if (session.UserId.HasValue)
- {
- dto.UserId = session.UserId.Value.ToString("N");
-
- var user = _userManager.GetUserById(session.UserId.Value);
-
- if (user != null)
- {
- dto.UserPrimaryImageTag = GetImageCacheTag(user, ImageType.Primary);
- }
- }
-
- return dto;
- }
-
private DtoOptions _itemInfoDtoOptions;
/// <summary>
@@ -1672,7 +1643,6 @@ namespace Emby.Server.Implementations.Session
var fields = dtoOptions.Fields.ToList();
fields.Remove(ItemFields.BasicSyncInfo);
- fields.Remove(ItemFields.SyncInfo);
fields.Remove(ItemFields.CanDelete);
fields.Remove(ItemFields.CanDownload);
fields.Remove(ItemFields.ChildCount);
@@ -1682,7 +1652,6 @@ namespace Emby.Server.Implementations.Session
fields.Remove(ItemFields.DateLastSaved);
fields.Remove(ItemFields.DisplayPreferencesId);
fields.Remove(ItemFields.Etag);
- fields.Remove(ItemFields.ExternalEtag);
fields.Remove(ItemFields.InheritedParentalRatingValue);
fields.Remove(ItemFields.ItemCounts);
fields.Remove(ItemFields.MediaSourceCount);
@@ -1698,8 +1667,7 @@ namespace Emby.Server.Implementations.Session
fields.Remove(ItemFields.Settings);
fields.Remove(ItemFields.SortName);
fields.Remove(ItemFields.Tags);
- fields.Remove(ItemFields.ThemeSongIds);
- fields.Remove(ItemFields.ThemeVideoIds);
+ fields.Remove(ItemFields.ExtraIds);
dtoOptions.Fields = fields.ToArray(fields.Count);
@@ -1729,14 +1697,9 @@ namespace Emby.Server.Implementations.Session
}
}
- private string GetDtoId(BaseItem item)
- {
- return _dtoService.GetDtoId(item);
- }
-
public void ReportNowViewingItem(string sessionId, string itemId)
{
- if (string.IsNullOrWhiteSpace(itemId))
+ if (string.IsNullOrEmpty(itemId))
{
throw new ArgumentNullException("itemId");
}
@@ -1776,46 +1739,31 @@ namespace Emby.Server.Implementations.Session
string.Equals(i.Client, client));
}
- public Task<SessionInfo> GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)
+ public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
- var user = string.IsNullOrWhiteSpace(info.UserId)
+ var user = info.UserId.Equals(Guid.Empty)
? null
: _userManager.GetUserById(info.UserId);
- appVersion = string.IsNullOrWhiteSpace(appVersion)
+ appVersion = string.IsNullOrEmpty(appVersion)
? info.AppVersion
: appVersion;
var deviceName = info.DeviceName;
var appName = info.AppName;
- if (!string.IsNullOrWhiteSpace(deviceId))
- {
- // Replace the info from the token with more recent info
- var device = _deviceManager.GetDevice(deviceId);
- if (device != null)
- {
- deviceName = device.Name;
- appName = device.AppName;
-
- if (!string.IsNullOrWhiteSpace(device.AppVersion))
- {
- appVersion = device.AppVersion;
- }
- }
- }
- else
+ if (string.IsNullOrEmpty(deviceId))
{
deviceId = info.DeviceId;
}
// Prevent argument exception
- if (string.IsNullOrWhiteSpace(appVersion))
+ if (string.IsNullOrEmpty(appVersion))
{
appVersion = "1";
}
@@ -1823,7 +1771,7 @@ namespace Emby.Server.Implementations.Session
return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user);
}
- public Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
+ public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
{
var result = _authRepo.Get(new AuthenticationInfoQuery
{
@@ -1834,7 +1782,7 @@ namespace Emby.Server.Implementations.Session
if (info == null)
{
- return Task.FromResult<SessionInfo>(null);
+ return null;
}
return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
@@ -1842,51 +1790,115 @@ namespace Emby.Server.Implementations.Session
public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
{
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList();
+ CheckDisposed();
+
+ var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
- public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data,
- CancellationToken cancellationToken)
+ public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+
+ var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList();
+
+ if (sessions.Count == 0)
+ {
+ return Task.CompletedTask;
+ }
+
+ var data = dataFn();
+
+ var tasks = sessions.Select(session => Task.Run(async () =>
+ {
+ try
+ {
+ await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending message", ex);
+ }
+
+ }, cancellationToken)).ToArray();
+
+ return Task.WhenAll(tasks);
+ }
+
+ public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+
+ var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList();
+
+ var tasks = sessions.Select(session => Task.Run(async () =>
+ {
+ try
+ {
+ await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending message", ex);
+ }
+
+ }, cancellationToken)).ToArray();
+
+ return Task.WhenAll(tasks);
+ }
+
+ public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
{
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList();
+ CheckDisposed();
+
+ var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
- await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error sending message", ex);
}
- }, cancellationToken));
+ }, cancellationToken)).ToArray();
return Task.WhenAll(tasks);
}
- public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data,
- CancellationToken cancellationToken)
+ public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
{
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
+ CheckDisposed();
+
+ var sessions = Sessions
+ .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i))
+ .ToList();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
- await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error sending message", ex);
}
- }, cancellationToken));
+ }, cancellationToken)).ToArray();
return Task.WhenAll(tasks);
}
+
+ private bool IsAdminSession(SessionInfo s)
+ {
+ var user = _userManager.GetUserById(s.UserId);
+
+ return user != null && user.Policy.IsAdministrator;
+ }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index a5af843db..9ab4753fb 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -19,11 +19,6 @@ namespace Emby.Server.Implementations.Session
public class SessionWebSocketListener : IWebSocketListener, IDisposable
{
/// <summary>
- /// The _true task result
- /// </summary>
- private readonly Task _trueTaskResult = Task.FromResult(true);
-
- /// <summary>
/// The _session manager
/// </summary>
private readonly ISessionManager _sessionManager;
@@ -39,7 +34,6 @@ namespace Emby.Server.Implementations.Session
private readonly IJsonSerializer _json;
private readonly IHttpServer _httpServer;
- private readonly IServerManager _serverManager;
/// <summary>
@@ -50,32 +44,22 @@ namespace Emby.Server.Implementations.Session
/// <param name="json">The json.</param>
/// <param name="httpServer">The HTTP server.</param>
/// <param name="serverManager">The server manager.</param>
- public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IJsonSerializer json, IHttpServer httpServer, IServerManager serverManager)
+ public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IJsonSerializer json, IHttpServer httpServer)
{
_sessionManager = sessionManager;
_logger = logManager.GetLogger(GetType().Name);
_json = json;
_httpServer = httpServer;
- _serverManager = serverManager;
- serverManager.WebSocketConnected += _serverManager_WebSocketConnected;
+ httpServer.WebSocketConnected += _serverManager_WebSocketConnected;
}
- async void _serverManager_WebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
+ void _serverManager_WebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
{
- var session = await GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint).ConfigureAwait(false);
+ var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint);
if (session != null)
{
- var controller = session.SessionController as WebSocketController;
-
- if (controller == null)
- {
- controller = new WebSocketController(session, _logger, _sessionManager);
- }
-
- controller.AddWebSocket(e.Argument);
-
- session.SessionController = controller;
+ EnsureController(session, e.Argument);
}
else
{
@@ -83,7 +67,7 @@ namespace Emby.Server.Implementations.Session
}
}
- private Task<SessionInfo> GetSession(QueryParamCollection queryString, string remoteEndpoint)
+ private SessionInfo GetSession(QueryParamCollection queryString, string remoteEndpoint)
{
if (queryString == null)
{
@@ -93,7 +77,7 @@ namespace Emby.Server.Implementations.Session
var token = queryString["api_key"];
if (string.IsNullOrWhiteSpace(token))
{
- return Task.FromResult<SessionInfo>(null);
+ return null;
}
var deviceId = queryString["deviceId"];
return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
@@ -101,8 +85,7 @@ namespace Emby.Server.Implementations.Session
public void Dispose()
{
- _serverManager.WebSocketConnected -= _serverManager_WebSocketConnected;
- GC.SuppressFinalize(this);
+ _httpServer.WebSocketConnected -= _serverManager_WebSocketConnected;
}
/// <summary>
@@ -112,350 +95,15 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
public Task ProcessMessage(WebSocketMessageInfo message)
{
- if (string.Equals(message.MessageType, "Identity", StringComparison.OrdinalIgnoreCase))
- {
- ProcessIdentityMessage(message);
- }
- else if (string.Equals(message.MessageType, "Context", StringComparison.OrdinalIgnoreCase))
- {
- ProcessContextMessage(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackStart(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackProgress(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackStopped", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackStopped(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackStart", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackStart(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackProgress", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackProgress(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackStopped", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackStopped(message);
- }
-
- return _trueTaskResult;
- }
-
- /// <summary>
- /// Processes the identity message.
- /// </summary>
- /// <param name="message">The message.</param>
- private async void ProcessIdentityMessage(WebSocketMessageInfo message)
- {
- _logger.Debug("Received Identity message: " + message.Data);
-
- var vals = message.Data.Split('|');
-
- if (vals.Length < 3)
- {
- _logger.Error("Client sent invalid identity message.");
- return;
- }
-
- var client = vals[0];
- var deviceId = vals[1];
- var version = vals[2];
- var deviceName = vals.Length > 3 ? vals[3] : string.Empty;
-
- var session = _sessionManager.GetSession(deviceId, client, version);
-
- if (session == null && !string.IsNullOrEmpty(deviceName))
- {
- _logger.Debug("Logging session activity");
-
- session = await _sessionManager.LogSessionActivity(client, version, deviceId, deviceName, message.Connection.RemoteEndPoint, null).ConfigureAwait(false);
- }
-
- if (session != null)
- {
- var controller = session.SessionController as WebSocketController;
-
- if (controller == null)
- {
- controller = new WebSocketController(session, _logger, _sessionManager);
- }
-
- controller.AddWebSocket(message.Connection);
-
- session.SessionController = controller;
- }
- else
- {
- _logger.Warn("Unable to determine session based on identity message: {0}", message.Data);
- }
- }
-
- /// <summary>
- /// Processes the context message.
- /// </summary>
- /// <param name="message">The message.</param>
- private void ProcessContextMessage(WebSocketMessageInfo message)
- {
- var session = GetSessionFromMessage(message);
-
- if (session != null)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[1];
-
- if (!string.IsNullOrWhiteSpace(itemId))
- {
- _sessionManager.ReportNowViewingItem(session.Id, itemId);
- }
- }
- }
-
- /// <summary>
- /// Gets the session from message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionFromMessage(WebSocketMessageInfo message)
- {
- var result = _sessionManager.Sessions.FirstOrDefault(i =>
- {
- var controller = i.SessionController as WebSocketController;
-
- if (controller != null)
- {
- if (controller.Sockets.Any(s => s.Id == message.Connection.Id))
- {
- return true;
- }
- }
-
- return false;
-
- });
-
- if (result == null)
- {
- _logger.Error("Unable to find session based on web socket message");
- }
-
- return result;
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Reports the playback start.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackStart(WebSocketMessageInfo message)
- {
- _logger.Debug("Received PlaybackStart message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- var canSeek = true;
-
- if (vals.Length > 1)
- {
- canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase);
- }
- if (vals.Length > 2)
- {
- // vals[2] used to be QueueableMediaTypes
- }
-
- var info = new PlaybackStartInfo
- {
- CanSeek = canSeek,
- ItemId = itemId,
- SessionId = session.Id
- };
-
- if (vals.Length > 3)
- {
- info.MediaSourceId = vals[3];
- }
-
- if (vals.Length > 4 && !string.IsNullOrWhiteSpace(vals[4]))
- {
- info.AudioStreamIndex = int.Parse(vals[4], _usCulture);
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[5]))
- {
- info.SubtitleStreamIndex = int.Parse(vals[5], _usCulture);
- }
-
- _sessionManager.OnPlaybackStart(info);
- }
+ return Task.CompletedTask;
}
- private void ReportPlaybackStart(WebSocketMessageInfo message)
+ private void EnsureController(SessionInfo session, IWebSocketConnection connection)
{
- _logger.Debug("Received ReportPlaybackStart message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackStartInfo>(message.Data);
+ var controllerInfo = session.EnsureController<WebSocketController>(s => new WebSocketController(s, _logger, _sessionManager));
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackStart(info);
- }
- }
-
- private void ReportPlaybackProgress(WebSocketMessageInfo message)
- {
- //_logger.Debug("Received ReportPlaybackProgress message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackProgressInfo>(message.Data);
-
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackProgress(info);
- }
- }
-
- /// <summary>
- /// Reports the playback progress.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackProgress(WebSocketMessageInfo message)
- {
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- long? positionTicks = null;
-
- if (vals.Length > 1)
- {
- long pos;
-
- if (long.TryParse(vals[1], out pos))
- {
- positionTicks = pos;
- }
- }
-
- var isPaused = vals.Length > 2 && string.Equals(vals[2], "true", StringComparison.OrdinalIgnoreCase);
- var isMuted = vals.Length > 3 && string.Equals(vals[3], "true", StringComparison.OrdinalIgnoreCase);
-
- var info = new PlaybackProgressInfo
- {
- ItemId = itemId,
- PositionTicks = positionTicks,
- IsMuted = isMuted,
- IsPaused = isPaused,
- SessionId = session.Id
- };
-
- if (vals.Length > 4)
- {
- info.MediaSourceId = vals[4];
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[5]))
- {
- info.VolumeLevel = int.Parse(vals[5], _usCulture);
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[6]))
- {
- info.AudioStreamIndex = int.Parse(vals[6], _usCulture);
- }
-
- if (vals.Length > 7 && !string.IsNullOrWhiteSpace(vals[7]))
- {
- info.SubtitleStreamIndex = int.Parse(vals[7], _usCulture);
- }
-
- _sessionManager.OnPlaybackProgress(info);
- }
- }
-
- private void ReportPlaybackStopped(WebSocketMessageInfo message)
- {
- _logger.Debug("Received ReportPlaybackStopped message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackStopInfo>(message.Data);
-
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackStopped(info);
- }
- }
-
- /// <summary>
- /// Reports the playback stopped.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackStopped(WebSocketMessageInfo message)
- {
- _logger.Debug("Received PlaybackStopped message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- long? positionTicks = null;
-
- if (vals.Length > 1)
- {
- long pos;
-
- if (long.TryParse(vals[1], out pos))
- {
- positionTicks = pos;
- }
- }
-
- var info = new PlaybackStopInfo
- {
- ItemId = itemId,
- PositionTicks = positionTicks,
- SessionId = session.Id
- };
-
- if (vals.Length > 2)
- {
- info.MediaSourceId = vals[2];
- }
-
- _sessionManager.OnPlaybackStopped(info);
- }
+ var controller = (WebSocketController)controllerInfo.Item1;
+ controller.AddWebSocket(connection);
}
}
}
diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs
index b13eb6116..ddac9660f 100644
--- a/Emby.Server.Implementations/Session/WebSocketController.cs
+++ b/Emby.Server.Implementations/Session/WebSocketController.cs
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using System.Net.WebSockets;
namespace Emby.Server.Implementations.Session
{
@@ -40,28 +41,14 @@ namespace Emby.Server.Implementations.Session
get { return HasOpenSockets; }
}
- private bool _isActive;
- private DateTime _lastActivityDate;
public bool IsSessionActive
{
get
{
- if (HasOpenSockets)
- {
- return true;
- }
-
- //return false;
- return _isActive && (DateTime.UtcNow - _lastActivityDate).TotalMinutes <= 10;
+ return HasOpenSockets;
}
}
- public void OnActivity()
- {
- _isActive = true;
- _lastActivityDate = DateTime.UtcNow;
- }
-
private IEnumerable<IWebSocketConnection> GetActiveSockets()
{
return Sockets
@@ -81,209 +68,40 @@ namespace Emby.Server.Implementations.Session
void connection_Closed(object sender, EventArgs e)
{
- if (!GetActiveSockets().Any())
- {
- _isActive = false;
+ var connection = (IWebSocketConnection)sender;
+ var sockets = Sockets.ToList();
+ sockets.Remove(connection);
- try
- {
- _sessionManager.ReportSessionEnded(Session.Id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reporting session ended.", ex);
- }
- }
+ Sockets = sockets;
+
+ _sessionManager.CloseIfNeeded(Session);
}
- private IWebSocketConnection GetActiveSocket()
+ public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
{
var socket = GetActiveSockets()
.FirstOrDefault();
if (socket == null)
{
- throw new InvalidOperationException("The requested session does not have an open web socket.");
+ return Task.CompletedTask;
}
- return socket;
- }
-
- public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<PlayRequest>
- {
- MessageType = "Play",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<PlaystateRequest>
- {
- MessageType = "Playstate",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<LibraryUpdateInfo>
- {
- MessageType = "LibraryChanged",
- Data = info
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the restart required message.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<string>
- {
- MessageType = "RestartRequired",
- Data = string.Empty
-
- }, cancellationToken);
- }
-
-
- /// <summary>
- /// Sends the user data change info.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<UserDataChangeInfo>
- {
- MessageType = "UserDataChanged",
- Data = info
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the server shutdown notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<string>
- {
- MessageType = "ServerShuttingDown",
- Data = string.Empty
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the server restart notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<string>
- {
- MessageType = "ServerRestarting",
- Data = string.Empty
-
- }, cancellationToken);
- }
-
- public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<GeneralCommand>
- {
- MessageType = "GeneralCommand",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "SessionEnded",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "PlaybackStart",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "PlaybackStopped",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<T>
+ return socket.SendAsync(new WebSocketMessage<T>
{
Data = data,
- MessageType = name
+ MessageType = name,
+ MessageId = messageId
}, cancellationToken);
}
- private Task SendMessageInternal<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- var socket = GetActiveSocket();
-
- return socket.SendAsync(message, cancellationToken);
- }
-
- private Task SendMessagesInternal<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- var tasks = GetActiveSockets().Select(i => Task.Run(async () =>
- {
- try
- {
- await i.SendAsync(message, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
public void Dispose()
{
foreach (var socket in Sockets.ToList())
{
socket.Closed -= connection_Closed;
}
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Social/SharingManager.cs b/Emby.Server.Implementations/Social/SharingManager.cs
deleted file mode 100644
index 23ce7492a..000000000
--- a/Emby.Server.Implementations/Social/SharingManager.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Social;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Emby.Server.Implementations.Social
-{
- public class SharingManager : ISharingManager
- {
- private readonly ISharingRepository _repository;
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerApplicationHost _appHost;
-
- public SharingManager(ISharingRepository repository, IServerConfigurationManager config, ILibraryManager libraryManager, IServerApplicationHost appHost)
- {
- _repository = repository;
- _config = config;
- _libraryManager = libraryManager;
- _appHost = appHost;
- }
-
- public async Task<SocialShareInfo> CreateShare(string itemId, string userId)
- {
- if (string.IsNullOrWhiteSpace(itemId))
- {
- throw new ArgumentNullException("itemId");
- }
- if (string.IsNullOrWhiteSpace(userId))
- {
- throw new ArgumentNullException("userId");
- }
-
- var item = _libraryManager.GetItemById(itemId);
-
- if (item == null)
- {
- throw new ResourceNotFoundException();
- }
-
- var externalUrl = (await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false)).WanAddress;
-
- if (string.IsNullOrWhiteSpace(externalUrl))
- {
- throw new InvalidOperationException("No external server address is currently available.");
- }
-
- var info = new SocialShareInfo
- {
- Id = Guid.NewGuid().ToString("N"),
- ExpirationDate = DateTime.UtcNow.AddDays(_config.Configuration.SharingExpirationDays),
- ItemId = itemId,
- UserId = userId
- };
-
- AddShareInfo(info, externalUrl);
-
- _repository.CreateShare(info);
-
- return info;
- }
-
- private string GetTitle(BaseItem item)
- {
- return item.Name;
- }
-
- public SocialShareInfo GetShareInfo(string id)
- {
- var info = _repository.GetShareInfo(id);
-
- AddShareInfo(info, _appHost.GetPublicSystemInfo(CancellationToken.None).Result.WanAddress);
-
- return info;
- }
-
- private void AddShareInfo(SocialShareInfo info, string externalUrl)
- {
- info.ImageUrl = externalUrl + "/Social/Shares/Public/" + info.Id + "/Image";
- info.Url = externalUrl + "/emby/web/shared.html?id=" + info.Id;
-
- var item = _libraryManager.GetItemById(info.ItemId);
-
- if (item != null)
- {
- info.Overview = item.Overview;
- info.Name = GetTitle(item);
- }
- }
-
- public void DeleteShare(string id)
- {
- _repository.DeleteShare(id);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Social/SharingRepository.cs b/Emby.Server.Implementations/Social/SharingRepository.cs
deleted file mode 100644
index 3c9e1024f..000000000
--- a/Emby.Server.Implementations/Social/SharingRepository.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Emby.Server.Implementations.Data;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Social;
-using SQLitePCL.pretty;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-
-namespace Emby.Server.Implementations.Social
-{
- public class SharingRepository : BaseSqliteRepository, ISharingRepository
- {
- protected IFileSystem FileSystem { get; private set; }
-
- public SharingRepository(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem)
- : base(logger)
- {
- FileSystem = fileSystem;
- DbFilePath = Path.Combine(appPaths.DataPath, "shares.db");
- }
-
- public void Initialize()
- {
- try
- {
- InitializeInternal();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
-
- FileSystem.DeleteFile(DbFilePath);
-
- InitializeInternal();
- }
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- private void InitializeInternal()
- {
- using (var connection = CreateConnection())
- {
- RunDefaultInitialization(connection);
-
- string[] queries = {
-
- "create table if not exists Shares (Id GUID NOT NULL, ItemId TEXT NOT NULL, UserId TEXT NOT NULL, ExpirationDate DateTime NOT NULL, PRIMARY KEY (Id))",
- "create index if not exists idx_Shares on Shares(Id)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries);
- }
- }
-
- public void CreateShare(SocialShareInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
- if (string.IsNullOrWhiteSpace(info.Id))
- {
- throw new ArgumentNullException("info.Id");
- }
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(db =>
- {
- var commandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (?, ?, ?, ?)";
-
- db.Execute(commandText,
- info.Id.ToGuidBlob(),
- info.ItemId,
- info.UserId,
- info.ExpirationDate.ToDateTimeParamValue());
- }, TransactionMode);
- }
- }
- }
-
- public SocialShareInfo GetShareInfo(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
-
- var paramList = new List<object>();
- paramList.Add(id.ToGuidBlob());
-
- foreach (var row in connection.Query(commandText, paramList.ToArray(paramList.Count)))
- {
- return GetSocialShareInfo(row);
- }
- }
- }
-
- return null;
- }
-
- private SocialShareInfo GetSocialShareInfo(IReadOnlyList<IResultSetValue> reader)
- {
- var info = new SocialShareInfo();
-
- info.Id = reader[0].ReadGuidFromBlob().ToString("N");
- info.ItemId = reader[1].ToString();
- info.UserId = reader[2].ToString();
- info.ExpirationDate = reader[3].ReadDateTime();
-
- return info;
- }
-
- public void DeleteShare(string id)
- {
-
- }
- }
-}
diff --git a/Emby.Server.Implementations/SystemEvents.cs b/Emby.Server.Implementations/SystemEvents.cs
index dfff92f1e..c1e6dc1da 100644
--- a/Emby.Server.Implementations/SystemEvents.cs
+++ b/Emby.Server.Implementations/SystemEvents.cs
@@ -17,34 +17,6 @@ namespace Emby.Server.Implementations
public SystemEvents(ILogger logger)
{
_logger = logger;
- Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- Microsoft.Win32.SystemEvents.SessionEnding += SystemEvents_SessionEnding;
- }
-
- private void SystemEvents_SessionEnding(object sender, Microsoft.Win32.SessionEndingEventArgs e)
- {
- switch (e.Reason)
- {
- case Microsoft.Win32.SessionEndReasons.Logoff:
- EventHelper.FireEventIfNotNull(SessionLogoff, this, EventArgs.Empty, _logger);
- break;
- case Microsoft.Win32.SessionEndReasons.SystemShutdown:
- EventHelper.FireEventIfNotNull(SystemShutdown, this, EventArgs.Empty, _logger);
- break;
- }
- }
-
- private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e)
- {
- switch (e.Mode)
- {
- case Microsoft.Win32.PowerModes.Resume:
- EventHelper.FireEventIfNotNull(Resume, this, EventArgs.Empty, _logger);
- break;
- case Microsoft.Win32.PowerModes.Suspend:
- EventHelper.FireEventIfNotNull(Suspend, this, EventArgs.Empty, _logger);
- break;
- }
}
}
}
diff --git a/Emby.Server.Implementations/TV/SeriesPostScanTask.cs b/Emby.Server.Implementations/TV/SeriesPostScanTask.cs
deleted file mode 100644
index 764df8baf..000000000
--- a/Emby.Server.Implementations/TV/SeriesPostScanTask.cs
+++ /dev/null
@@ -1,241 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Threading;
-using MediaBrowser.Model.Xml;
-using MediaBrowser.Providers.TV;
-
-namespace Emby.Server.Implementations.TV
-{
- class SeriesGroup : List<Series>, IGrouping<string, Series>
- {
- public string Key { get; set; }
- }
-
- class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILocalizationManager _localization;
- private readonly IFileSystem _fileSystem;
- private readonly IXmlReaderSettingsFactory _xmlSettings;
-
- public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _config = config;
- _localization = localization;
- _fileSystem = fileSystem;
- _xmlSettings = xmlSettings;
- }
-
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return RunInternal(progress, cancellationToken);
- }
-
- private Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- GroupByPresentationUniqueKey = false,
- DtoOptions = new DtoOptions(true)
-
- }).Cast<Series>().ToList();
-
- var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
-
- return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings).Run(seriesGroups, true, cancellationToken);
- }
-
- internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
- {
- var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
-
- var visited = new HashSet<Series>();
-
- foreach (var series in seriesList)
- {
- if (!visited.Contains(series))
- {
- var group = new SeriesGroup();
- FindAllLinked(series, visited, links, group);
-
- group.Key = group.Select(s => s.PresentationUniqueKey).FirstOrDefault(id => !string.IsNullOrEmpty(id));
-
- yield return group;
- }
- }
- }
-
- private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
- {
- results.Add(series);
- visited.Add(series);
-
- var links = linksMap[series];
-
- foreach (var s in links)
- {
- if (!visited.Contains(s))
- {
- FindAllLinked(s, visited, linksMap, results);
- }
- }
- }
-
- private static bool ShareProviderId(Series a, Series b)
- {
- return string.Equals(a.PresentationUniqueKey, b.PresentationUniqueKey, StringComparison.Ordinal);
- }
-
- public int Order
- {
- get
- {
- // Run after tvdb update task
- return 1;
- }
- }
- }
-
- public class CleanMissingEpisodesEntryPoint : IServerEntryPoint
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILocalizationManager _localization;
- private readonly IFileSystem _fileSystem;
- private readonly object _libraryChangedSyncLock = new object();
- private const int LibraryUpdateDuration = 180000;
- private readonly ITaskManager _taskManager;
- private readonly IXmlReaderSettingsFactory _xmlSettings;
- private readonly ITimerFactory _timerFactory;
-
- public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager, IXmlReaderSettingsFactory xmlSettings, ITimerFactory timerFactory)
- {
- _libraryManager = libraryManager;
- _config = config;
- _logger = logger;
- _localization = localization;
- _fileSystem = fileSystem;
- _taskManager = taskManager;
- _xmlSettings = xmlSettings;
- _timerFactory = timerFactory;
- }
-
- private ITimer LibraryUpdateTimer { get; set; }
-
- public void Run()
- {
- _libraryManager.ItemAdded += _libraryManager_ItemAdded;
- }
-
- private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
- }
- }
- }
-
- private async void LibraryUpdateTimerCallback(object state)
- {
- try
- {
- if (MissingEpisodeProvider.IsRunning)
- {
- return;
- }
-
- if (_libraryManager.IsScanRunning)
- {
- return;
- }
-
- var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- GroupByPresentationUniqueKey = false,
- DtoOptions = new DtoOptions(true)
-
- }).Cast<Series>().ToList();
-
- var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
-
- await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings)
- .Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SeriesPostScanTask", ex);
- }
- }
-
- private bool FilterItem(BaseItem item)
- {
- return item is Episode && item.LocationType != LocationType.Virtual;
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </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)
- {
- if (dispose)
- {
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
-
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index d92245a67..1f9cb9164 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -37,48 +37,50 @@ namespace Emby.Server.Implementations.TV
}
string presentationUniqueKey = null;
- int? limit = null;
- if (!string.IsNullOrWhiteSpace(request.SeriesId))
+ if (!string.IsNullOrEmpty(request.SeriesId))
{
var series = _libraryManager.GetItemById(request.SeriesId) as Series;
if (series != null)
{
presentationUniqueKey = GetUniqueSeriesKey(series);
- limit = 1;
}
}
- if (!string.IsNullOrWhiteSpace(presentationUniqueKey))
+ if (!string.IsNullOrEmpty(presentationUniqueKey))
{
return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
}
- var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+ var parentIdGuid = string.IsNullOrEmpty(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
- List<BaseItem> parents;
+ BaseItem[] parents;
if (parentIdGuid.HasValue)
{
var parent = _libraryManager.GetItemById(parentIdGuid.Value);
- parents = new List<BaseItem>();
+
if (parent != null)
{
- parents.Add(parent);
+ parents = new[] { parent };
+ }
+ else
+ {
+ parents = Array.Empty<BaseItem>();
}
}
else
{
- parents = user.RootFolder.GetChildren(user, true)
+ parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
- .ToList();
+ .ToArray();
}
return GetNextUp(request, parents, dtoOptions);
}
- public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<BaseItem> parentsFolders, DtoOptions dtoOptions)
+ public QueryResult<BaseItem> GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions dtoOptions)
{
var user = _userManager.GetUserById(request.UserId);
@@ -89,7 +91,7 @@ namespace Emby.Server.Implementations.TV
string presentationUniqueKey = null;
int? limit = null;
- if (!string.IsNullOrWhiteSpace(request.SeriesId))
+ if (!string.IsNullOrEmpty(request.SeriesId))
{
var series = _libraryManager.GetItemById(request.SeriesId) as Series;
@@ -100,7 +102,7 @@ namespace Emby.Server.Implementations.TV
}
}
- if (!string.IsNullOrWhiteSpace(presentationUniqueKey))
+ if (!string.IsNullOrEmpty(presentationUniqueKey))
{
return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
}
@@ -113,7 +115,7 @@ namespace Emby.Server.Implementations.TV
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Episode).Name },
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
SeriesPresentationUniqueKey = presentationUniqueKey,
Limit = limit,
DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
@@ -126,7 +128,7 @@ namespace Emby.Server.Implementations.TV
},
GroupBySeriesPresentationUniqueKey = true
- }, parentsFolders).Cast<Episode>().Select(GetUniqueSeriesKey);
+ }, parentsFolders.ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
@@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.TV
// If viewing all next up for all series, remove first episodes
// But if that returns empty, keep those first episodes (avoid completely empty view)
- var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId);
+ var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId);
var anyFound = false;
return allNextUp
@@ -190,7 +192,7 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { typeof(Episode).Name },
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
ParentIndexNumberNotEquals = 0,
@@ -212,7 +214,7 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { typeof(Episode).Name },
- OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
+ OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
Limit = 1,
IsPlayed = false,
IsVirtualItem = false,
diff --git a/Emby.Server.Implementations/TextEncoding/TextEncoding.cs b/Emby.Server.Implementations/TextEncoding/TextEncoding.cs
index 9eb9be7ea..ea87a9539 100644
--- a/Emby.Server.Implementations/TextEncoding/TextEncoding.cs
+++ b/Emby.Server.Implementations/TextEncoding/TextEncoding.cs
@@ -193,6 +193,8 @@ namespace Emby.Server.Implementations.TextEncoding
switch (language.ToLower())
{
+ case "tha":
+ return "windows-874";
case "hun":
return "windows-1252";
case "pol":
@@ -203,6 +205,7 @@ namespace Emby.Server.Implementations.TextEncoding
case "hrv":
case "rum":
case "ron":
+ case "rom":
case "rup":
return "windows-1250";
// albanian
diff --git a/Emby.Server.Implementations/Threading/CommonTimer.cs b/Emby.Server.Implementations/Threading/CommonTimer.cs
index bb67325d1..9451b07f3 100644
--- a/Emby.Server.Implementations/Threading/CommonTimer.cs
+++ b/Emby.Server.Implementations/Threading/CommonTimer.cs
@@ -31,7 +31,6 @@ namespace Emby.Server.Implementations.Threading
public void Dispose()
{
_timer.Dispose();
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index 28de80da1..f195ca710 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -234,7 +234,6 @@ namespace Emby.Server.Implementations.Udp
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 51acfee88..435bcfd04 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -19,6 +19,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
+using MediaBrowser.Controller.Configuration;
namespace Emby.Server.Implementations.Updates
{
@@ -111,7 +112,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private readonly ISecurityManager _securityManager;
- private readonly IConfigurationManager _config;
+ private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -125,7 +126,7 @@ namespace Emby.Server.Implementations.Updates
// netframework or netcore
private readonly string _packageRuntime;
- public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, string packageRuntime)
+ public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IServerConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, string packageRuntime)
{
if (logger == null)
{
@@ -189,7 +190,7 @@ namespace Emby.Server.Implementations.Updates
{
cancellationToken.ThrowIfCancellationRequested();
- var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(json);
+ var packages = await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(json).ConfigureAwait(false);
return FilterPackages(packages, packageType, applicationVersion);
}
@@ -203,8 +204,6 @@ namespace Emby.Server.Implementations.Updates
}
}
- private DateTime _lastPackageUpdateTime;
-
/// <summary>
/// Gets all available packages.
/// </summary>
@@ -212,77 +211,21 @@ namespace Emby.Server.Implementations.Updates
/// <returns>Task{List{PackageInfo}}.</returns>
public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
{
- _logger.Info("Opening {0}", PackageCachePath);
- try
- {
- using (var stream = _fileSystem.OpenRead(PackageCachePath))
- {
- var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(stream);
-
- if (DateTime.UtcNow - _lastPackageUpdateTime > GetCacheLength())
- {
- UpdateCachedPackages(CancellationToken.None, false);
- }
-
- return FilterPackages(packages);
- }
- }
- catch (Exception)
- {
-
- }
-
- _lastPackageUpdateTime = DateTime.MinValue;
- await UpdateCachedPackages(cancellationToken, true).ConfigureAwait(false);
- using (var stream = _fileSystem.OpenRead(PackageCachePath))
- {
- return FilterPackages(_jsonSerializer.DeserializeFromStream<PackageInfo[]>(stream));
- }
- }
-
- private string PackageCachePath
- {
- get { return Path.Combine(_appPaths.CachePath, "serverpackages.json"); }
- }
-
- private readonly SemaphoreSlim _updateSemaphore = new SemaphoreSlim(1, 1);
- private async Task UpdateCachedPackages(CancellationToken cancellationToken, bool throwErrors)
- {
- await _updateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
+ using (var response = await _httpClient.SendAsync(new HttpRequestOptions
{
- if (DateTime.UtcNow - _lastPackageUpdateTime < GetCacheLength())
- {
- return;
- }
-
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = "https://www.mb3admin.com/admin/service/EmbyPackages.json",
- CancellationToken = cancellationToken,
- Progress = new SimpleProgress<Double>()
-
- }).ConfigureAwait(false);
-
- _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(PackageCachePath));
+ Url = "https://www.mb3admin.com/admin/service/EmbyPackages.json",
+ CancellationToken = cancellationToken,
+ Progress = new SimpleProgress<Double>(),
+ CacheLength = GetCacheLength(),
+ CacheMode = CacheMode.Unconditional
- _fileSystem.CopyFile(tempFile, PackageCachePath, true);
- _lastPackageUpdateTime = DateTime.UtcNow;
- }
- catch (Exception ex)
+ }, "GET").ConfigureAwait(false))
{
- _logger.ErrorException("Error updating package cache", ex);
-
- if (throwErrors)
+ using (var stream = response.Content)
{
- throw;
+ return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
}
}
- finally
- {
- _updateSemaphore.Release();
- }
}
private PackageVersionClass GetSystemUpdateLevel()
@@ -304,12 +247,12 @@ namespace Emby.Server.Implementations.Updates
var versions = new List<PackageVersionInfo>();
foreach (var version in package.versions)
{
- if (string.IsNullOrWhiteSpace(version.sourceUrl))
+ if (string.IsNullOrEmpty(version.sourceUrl))
{
continue;
}
- if (string.IsNullOrWhiteSpace(version.runtimes) || version.runtimes.IndexOf(_packageRuntime, StringComparison.OrdinalIgnoreCase) == -1)
+ if (string.IsNullOrEmpty(version.runtimes) || version.runtimes.IndexOf(_packageRuntime, StringComparison.OrdinalIgnoreCase) == -1)
{
continue;
}
@@ -339,7 +282,7 @@ namespace Emby.Server.Implementations.Updates
var returnList = new List<PackageInfo>();
- var filterOnPackageType = !string.IsNullOrWhiteSpace(packageType);
+ var filterOnPackageType = !string.IsNullOrEmpty(packageType);
foreach (var p in packagesList)
{
@@ -465,7 +408,7 @@ namespace Emby.Server.Implementations.Updates
return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
}).Where(i => i != null)
- .Where(p => !string.IsNullOrWhiteSpace(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
+ .Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
}
/// <summary>
@@ -491,7 +434,7 @@ namespace Emby.Server.Implementations.Updates
var installationInfo = new InstallationInfo
{
- Id = Guid.NewGuid().ToString("N"),
+ Id = Guid.NewGuid(),
Name = package.name,
AssemblyGuid = package.guid,
UpdateClass = package.classification,
@@ -575,7 +518,6 @@ namespace Emby.Server.Implementations.Updates
finally
{
// Dispose the progress object and remove the installation from the in-progress list
- innerProgress.Dispose();
tuple.Item2.Dispose();
}
}
@@ -590,16 +532,23 @@ namespace Emby.Server.Implementations.Updates
/// <returns>Task.</returns>
private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
{
- // Do the install
- await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
+ IPlugin plugin = null;
- // Do plugin-specific processing
if (isPlugin)
{
// Set last update time if we were installed before
- var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
- ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
+ plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
+ ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
+ }
+
+ string targetPath = plugin == null ? null : plugin.AssemblyFilePath;
+
+ // Do the install
+ await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false);
+ // Do plugin-specific processing
+ if (isPlugin)
+ {
if (plugin != null)
{
OnPluginUpdated(plugin, package);
@@ -611,13 +560,17 @@ namespace Emby.Server.Implementations.Updates
}
}
- private async Task PerformPackageInstallation(IProgress<double> progress, PackageVersionInfo package, CancellationToken cancellationToken)
+ private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
{
// Target based on if it is an archive or single assembly
// zip archives are assumed to contain directory structures relative to our ProgramDataPath
var extension = Path.GetExtension(package.targetFilename);
var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase);
- var target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
+
+ if (target == null)
+ {
+ target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
+ }
// Download to temporary file so that, if interrupted, it won't destroy the existing installation
var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
@@ -632,7 +585,7 @@ namespace Emby.Server.Implementations.Updates
// Validate with a checksum
var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
- if (packageChecksum != Guid.Empty) // support for legacy uploads for now
+ if (!packageChecksum.Equals(Guid.Empty)) // support for legacy uploads for now
{
using (var stream = _fileSystem.OpenRead(tempFile))
{
@@ -700,6 +653,15 @@ namespace Emby.Server.Implementations.Updates
_fileSystem.DeleteFile(path);
+ var list = _config.Configuration.UninstalledPlugins.ToList();
+ var filename = Path.GetFileName(path);
+ if (!list.Contains(filename, StringComparer.OrdinalIgnoreCase))
+ {
+ list.Add(filename);
+ _config.Configuration.UninstalledPlugins = list.ToArray();
+ _config.SaveConfiguration();
+ }
+
OnPluginUninstalled(plugin);
_applicationHost.NotifyPendingRestart();
@@ -728,7 +690,6 @@ namespace Emby.Server.Implementations.Updates
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
index f051e856a..2543fd372 100644
--- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
@@ -28,120 +28,70 @@ namespace Emby.Server.Implementations.UserViews
{
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
var view = (CollectionFolder)item;
+ var viewType = view.CollectionType;
- var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ string[] includeItemTypes;
- var result = view.GetItemList(new InternalItemsQuery
+ if (string.Equals(viewType, CollectionType.Movies))
{
- CollapseBoxSetItems = false,
- Recursive = recursive,
- ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Playlist" },
- DtoOptions = new DtoOptions(false)
-
- });
-
- var items = result.Select(i =>
+ includeItemTypes = new string[] { "Movie" };
+ }
+ else if (string.Equals(viewType, CollectionType.TvShows))
{
- var episode = i as Episode;
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null)
- {
- return series;
- }
-
- return episode;
- }
-
- var season = i as Season;
- if (season != null)
- {
- var series = season.Series;
- if (series != null)
- {
- return series;
- }
-
- return season;
- }
-
- var audio = i as Audio;
- if (audio != null)
- {
- var album = audio.AlbumEntity;
- if (album != null && album.HasImage(ImageType.Primary))
- {
- return album;
- }
- }
-
- return i;
-
- }).DistinctBy(i => i.Id);
-
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
- }
-
- protected override bool Supports(IHasMetadata item)
- {
- return item is CollectionFolder;
- }
-
- protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- if (imageType == ImageType.Primary)
+ includeItemTypes = new string[] { "Series" };
+ }
+ else if (string.Equals(viewType, CollectionType.Music))
{
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- return CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540);
+ includeItemTypes = new string[] { "MusicAlbum" };
+ }
+ else if (string.Equals(viewType, CollectionType.Books))
+ {
+ includeItemTypes = new string[] { "Book", "AudioBook" };
+ }
+ else if (string.Equals(viewType, CollectionType.Games))
+ {
+ includeItemTypes = new string[] { "Game" };
+ }
+ else if (string.Equals(viewType, CollectionType.BoxSets))
+ {
+ includeItemTypes = new string[] { "BoxSet" };
+ }
+ else if (string.Equals(viewType, CollectionType.HomeVideos) || string.Equals(viewType, CollectionType.Photos))
+ {
+ includeItemTypes = new string[] { "Video", "Photo" };
+ }
+ else
+ {
+ includeItemTypes = new string[] { "Video", "Audio", "Photo", "Movie", "Series" };
}
- return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex);
- }
- }
-
- public class ManualCollectionFolderImageProvider : BaseDynamicImageProvider<ManualCollectionsFolder>
- {
- private readonly ILibraryManager _libraryManager;
-
- public ManualCollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- _libraryManager = libraryManager;
- }
-
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
- {
- var view = (ManualCollectionsFolder)item;
-
- var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ var recursive = !new[] { CollectionType.Playlists }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- var items = _libraryManager.GetItemList(new InternalItemsQuery
+ return view.GetItemList(new InternalItemsQuery
{
+ CollapseBoxSetItems = false,
Recursive = recursive,
- IncludeItemTypes = new[] { typeof(BoxSet).Name },
- Limit = 20,
- OrderBy = new [] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
- DtoOptions = new DtoOptions(false)
- });
+ DtoOptions = new DtoOptions(false),
+ ImageTypes = new ImageType[] { ImageType.Primary },
+ Limit = 8,
+ OrderBy = new ValueTuple<string, SortOrder>[]
+ {
+ new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
+ },
+ IncludeItemTypes = includeItemTypes
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
+ }).ToList();
}
- protected override bool Supports(IHasMetadata item)
+ protected override bool Supports(BaseItem item)
{
- return item is ManualCollectionsFolder;
+ return item is CollectionFolder;
}
- protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(BaseItem item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
@@ -158,5 +108,4 @@ namespace Emby.Server.Implementations.UserViews
return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex);
}
}
-
}
diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
index 23b8c9b9e..c75033261 100644
--- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -31,39 +31,12 @@ namespace Emby.Server.Implementations.UserViews
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
var view = (UserView)item;
- if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
- {
- var programs = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
- ImageTypes = new[] { ImageType.Primary },
- Limit = 30,
- IsMovie = true,
- DtoOptions = new DtoOptions(false)
-
- });
-
- return GetFinalItems(programs);
- }
-
- if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase))
- {
- var userItemsResult = view.GetItemList(new InternalItemsQuery
- {
- CollapseBoxSetItems = false,
- DtoOptions = new DtoOptions(false)
- });
-
- 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 recursive = isUsingCollectionStrip && !new[] { CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
var result = view.GetItemList(new InternalItemsQuery
{
@@ -116,13 +89,19 @@ namespace Emby.Server.Implementations.UserViews
if (isUsingCollectionStrip)
{
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)), 8);
+ return items
+ .Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb))
+ .OrderBy(i => Guid.NewGuid())
+ .ToList();
}
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary)));
+ return items
+ .Where(i => i.HasImage(ImageType.Primary))
+ .OrderBy(i => Guid.NewGuid())
+ .ToList();
}
- protected override bool Supports(IHasMetadata item)
+ protected override bool Supports(BaseItem item)
{
var view = item as UserView;
if (view != null)
@@ -139,14 +118,13 @@ namespace Emby.Server.Implementations.UserViews
{
CollectionType.Movies,
CollectionType.TvShows,
- CollectionType.Playlists,
- CollectionType.Photos
+ CollectionType.Playlists
};
return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
}
- protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(BaseItem item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
if (itemsWithImages.Count == 0)
{
diff --git a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs b/Emby.Server.Implementations/UserViews/FolderImageProvider.cs
index 80a74e877..abd6810b0 100644
--- a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/FolderImageProvider.cs
@@ -17,7 +17,7 @@ using MediaBrowser.Controller.Dto;
namespace Emby.Server.Implementations.Photos
{
public abstract class BaseFolderImageProvider<T> : BaseDynamicImageProvider<T>
- where T : Folder, new ()
+ where T : Folder, new()
{
protected ILibraryManager _libraryManager;
@@ -27,43 +27,33 @@ namespace Emby.Server.Implementations.Photos
_libraryManager = libraryManager;
}
- protected override List<BaseItem> GetItemsWithImages(IHasMetadata item)
+ protected override List<BaseItem> GetItemsWithImages(BaseItem item)
{
return _libraryManager.GetItemList(new InternalItemsQuery
{
- Parent = item as BaseItem,
- GroupByPresentationUniqueKey = false,
+ Parent = item,
DtoOptions = new DtoOptions(true),
- ImageTypes = new ImageType[] { ImageType.Primary }
+ ImageTypes = new ImageType[] { ImageType.Primary },
+ OrderBy = new System.ValueTuple<string, SortOrder>[]
+ {
+ new System.ValueTuple<string, SortOrder>(ItemSortBy.IsFolder, SortOrder.Ascending),
+ new System.ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending)
+ },
+ Limit = 1
});
}
- protected override string CreateImage(IHasMetadata item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ protected override string CreateImage(BaseItem item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
}
- protected override bool Supports(IHasMetadata item)
+ protected override bool Supports(BaseItem item)
{
- if (item is PhotoAlbum || item is MusicAlbum)
- {
- return true;
- }
-
- if (item.GetType() == typeof(Folder))
- {
- var folder = item as Folder;
- if (folder.IsTopParent)
- {
- return false;
- }
- return true;
- }
-
- return false;
+ return item is T;
}
- protected override bool HasChangedByDate(IHasMetadata item, ItemImageInfo image)
+ protected override bool HasChangedByDate(BaseItem item, ItemImageInfo image)
{
if (item is MusicAlbum)
{
@@ -80,6 +70,25 @@ namespace Emby.Server.Implementations.Photos
: base(fileSystem, providerManager, applicationPaths, imageProcessor, libraryManager)
{
}
+
+ protected override bool Supports(BaseItem item)
+ {
+ if (item is PhotoAlbum || item is MusicAlbum)
+ {
+ return false;
+ }
+
+ var folder = item as Folder;
+ if (folder != null)
+ {
+ if (folder.IsTopParent)
+ {
+ return false;
+ }
+ }
+ return true;
+ //return item.SourceType == SourceType.Library;
+ }
}
public class MusicAlbumImageProvider : BaseFolderImageProvider<MusicAlbum>
diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
deleted file mode 100644
index 6e68810d8..000000000
--- a/Emby.Server.Implementations/packages.config
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="Emby.XmlTv" version="1.0.10" targetFramework="net46" />
- <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
- <package id="SharpCompress" version="0.18.2" targetFramework="net46" />
- <package id="SimpleInjector" version="4.0.12" targetFramework="net46" />
- <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
- <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />
-</packages> \ No newline at end of file
diff --git a/Emby.Server.Implementations/values.txt b/Emby.Server.Implementations/values.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Emby.Server.Implementations/values.txt
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 04cef60bf..9676a2c2a 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -70,7 +70,7 @@ namespace MediaBrowser.Api
{
if (string.IsNullOrWhiteSpace(value))
{
- return new string[] { };
+ return Array.Empty<string>();
}
if (removeEmpty)
@@ -88,7 +88,6 @@ namespace MediaBrowser.Api
public void Dispose()
{
- GC.SuppressFinalize(this);
}
}
}
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 1629d49b4..db6af0c4b 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -55,7 +55,7 @@ namespace MediaBrowser.Api
return Request.Headers[name];
}
- private static readonly string[] EmptyStringArray = new string[] { };
+ private static readonly string[] EmptyStringArray = Array.Empty<string>();
public static string[] SplitValue(string value, char delim)
{
if (string.IsNullOrWhiteSpace(value))
@@ -65,7 +65,13 @@ namespace MediaBrowser.Api
return value.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries);
}
-
+
+ public static Guid[] GetGuids(string value)
+ {
+ // Unfortunately filtermenu.js was using |. This can be deprecated after a while.
+ return (value ?? string.Empty).Split(new[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries).Select(i => new Guid(i)).ToArray();
+ }
+
/// <summary>
/// To the optimized result.
/// </summary>
@@ -75,17 +81,17 @@ namespace MediaBrowser.Api
protected object ToOptimizedResult<T>(T result)
where T : class
{
- return ResultFactory.GetOptimizedResult(Request, result);
+ return ResultFactory.GetResult(Request, result);
}
- protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId, bool restrictUserPreferences)
+ protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, Guid userId, bool restrictUserPreferences)
{
var auth = authContext.GetAuthorizationInfo(Request);
- var authenticatedUser = userManager.GetUserById(auth.UserId);
+ var authenticatedUser = auth.User;
// If they're going to update the record of another user, they must be an administrator
- if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase))
+ if (!userId.Equals(auth.UserId))
{
if (!authenticatedUser.Policy.IsAdministrator)
{
@@ -102,24 +108,12 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// To the optimized serialized result using cache.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="result">The result.</param>
- /// <returns>System.Object.</returns>
- protected object ToOptimizedSerializedResultUsingCache<T>(T result)
- where T : class
- {
- return ToOptimizedResult(result);
- }
-
- /// <summary>
/// Gets the session.
/// </summary>
/// <returns>SessionInfo.</returns>
- protected async Task<SessionInfo> GetSession(ISessionContext sessionContext)
+ protected SessionInfo GetSession(ISessionContext sessionContext)
{
- var session = await sessionContext.GetSession(Request).ConfigureAwait(false);
+ var session = sessionContext.GetSession(Request);
if (session == null)
{
@@ -133,38 +127,37 @@ namespace MediaBrowser.Api
{
var options = new DtoOptions();
- var authInfo = authContext.GetAuthorizationInfo(Request);
-
- options.DeviceId = authInfo.DeviceId;
-
var hasFields = request as IHasItemFields;
if (hasFields != null)
{
options.Fields = hasFields.GetItemFields();
}
- var client = authInfo.Client ?? string.Empty;
- if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1)
+ if (!options.ContainsField(Model.Querying.ItemFields.RecursiveItemCount) || !options.ContainsField(Model.Querying.ItemFields.ChildCount))
{
- var list = options.Fields.ToList();
- list.Add(Model.Querying.ItemFields.RecursiveItemCount);
- options.Fields = list.ToArray(list.Count);
- }
+ var client = authContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
+ if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var list = options.Fields.ToList();
+ list.Add(Model.Querying.ItemFields.RecursiveItemCount);
+ options.Fields = list.ToArray(list.Count);
+ }
- if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
- client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
- {
- var list = options.Fields.ToList();
- list.Add(Model.Querying.ItemFields.ChildCount);
- options.Fields = list.ToArray(list.Count);
+ if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
+ client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var list = options.Fields.ToList();
+ list.Add(Model.Querying.ItemFields.ChildCount);
+ options.Fields = list.ToArray(list.Count);
+ }
}
var hasDtoOptions = request as IHasDtoOptions;
@@ -289,7 +282,7 @@ namespace MediaBrowser.Api
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
- }).OfType<Person>().FirstOrDefault();
+ }).OfType<T>().FirstOrDefault();
if (result == null)
{
@@ -299,7 +292,7 @@ namespace MediaBrowser.Api
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
- }).OfType<Person>().FirstOrDefault();
+ }).OfType<T>().FirstOrDefault();
}
if (result == null)
@@ -310,10 +303,10 @@ namespace MediaBrowser.Api
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
- }).OfType<Person>().FirstOrDefault();
+ }).OfType<T>().FirstOrDefault();
}
- return result as T;
+ return result;
}
protected string GetPathValue(int index)
@@ -331,7 +324,7 @@ namespace MediaBrowser.Api
return pathInfo[index];
}
- private static List<string> Parse(string pathUri)
+ private List<string> Parse(string pathUri)
{
var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None);
diff --git a/MediaBrowser.Api/BrandingService.cs b/MediaBrowser.Api/BrandingService.cs
index 4602f56ed..4eb69678a 100644
--- a/MediaBrowser.Api/BrandingService.cs
+++ b/MediaBrowser.Api/BrandingService.cs
@@ -26,17 +26,15 @@ namespace MediaBrowser.Api
public object Get(GetBrandingOptions request)
{
- var result = _config.GetConfiguration<BrandingOptions>("branding");
-
- return ToOptimizedResult(result);
+ return _config.GetConfiguration<BrandingOptions>("branding");
}
public object Get(GetBrandingCss request)
{
var result = _config.GetConfiguration<BrandingOptions>("branding");
- // When null this throws a 405 error under Mono OSX, so default to empty string
- return ResultFactory.GetResult(result.CustomCss ?? string.Empty, "text/css");
+ // When null this throws a 405 error under Mono OSX, so default to empty string
+ return ResultFactory.GetResult(Request, result.CustomCss ?? string.Empty, "text/css");
}
}
}
diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs
index 2e8eb9e07..0b9d5efa9 100644
--- a/MediaBrowser.Api/ChannelService.cs
+++ b/MediaBrowser.Api/ChannelService.cs
@@ -11,6 +11,8 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Model.Services;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
namespace MediaBrowser.Api
{
@@ -21,8 +23,8 @@ namespace MediaBrowser.Api
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -41,6 +43,8 @@ namespace MediaBrowser.Api
[ApiMember(Name = "SupportsLatestItems", Description = "Optional. Filter by channels that support getting latest items.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? SupportsLatestItems { get; set; }
+ public bool? SupportsMediaDeletion { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this instance is favorite.
/// </summary>
@@ -74,7 +78,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -122,7 +126,7 @@ namespace MediaBrowser.Api
/// Gets the order by.
/// </summary>
/// <returns>IEnumerable{ItemSortBy}.</returns>
- public Tuple<string, SortOrder>[] GetOrderBy()
+ public ValueTuple<string, SortOrder>[] GetOrderBy()
{
return BaseItemsRequest.GetOrderBy(SortBy, SortOrder);
}
@@ -136,7 +140,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -178,21 +182,16 @@ namespace MediaBrowser.Api
}
}
- [Route("/Channels/Folder", "GET", Summary = "Gets the users channel folder, along with configured images")]
- public class GetChannelFolder : IReturn<BaseItemDto>
- {
- [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
- }
-
[Authenticated]
public class ChannelService : BaseApiService
{
private readonly IChannelManager _channelManager;
+ private IUserManager _userManager;
- public ChannelService(IChannelManager channelManager)
+ public ChannelService(IChannelManager channelManager, IUserManager userManager)
{
_channelManager = channelManager;
+ _userManager = userManager;
}
public object Get(GetAllChannelFeatures request)
@@ -209,56 +208,132 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
- public object Get(GetChannelFolder request)
+ public object Get(GetChannels request)
{
- return ToOptimizedResult(_channelManager.GetChannelFolder(request.UserId, CancellationToken.None));
- }
-
- public async Task<object> Get(GetChannels request)
- {
- var result = await _channelManager.GetChannels(new ChannelQuery
+ var result = _channelManager.GetChannels(new ChannelQuery
{
Limit = request.Limit,
StartIndex = request.StartIndex,
UserId = request.UserId,
SupportsLatestItems = request.SupportsLatestItems,
+ SupportsMediaDeletion = request.SupportsMediaDeletion,
IsFavorite = request.IsFavorite
-
- }, CancellationToken.None).ConfigureAwait(false);
+ });
return ToOptimizedResult(result);
}
public async Task<object> Get(GetChannelItems request)
{
- var result = await _channelManager.GetChannelItems(new ChannelItemQuery
+ var user = request.UserId.Equals(Guid.Empty)
+ ? null
+ : _userManager.GetUserById(request.UserId);
+
+ var query = new InternalItemsQuery(user)
{
Limit = request.Limit,
StartIndex = request.StartIndex,
- UserId = request.UserId,
- ChannelId = request.Id,
- FolderId = request.FolderId,
+ ChannelIds = new Guid[] { new Guid(request.Id) },
+ ParentId = string.IsNullOrWhiteSpace(request.FolderId) ? Guid.Empty : new Guid(request.FolderId),
OrderBy = request.GetOrderBy(),
- Filters = request.GetFilters().ToArray(),
- Fields = request.GetItemFields()
+ DtoOptions = new Controller.Dto.DtoOptions
+ {
+ Fields = request.GetItemFields()
+ }
+
+ };
+
+ foreach (var filter in request.GetFilters())
+ {
+ switch (filter)
+ {
+ case ItemFilter.Dislikes:
+ query.IsLiked = false;
+ break;
+ case ItemFilter.IsFavorite:
+ query.IsFavorite = true;
+ break;
+ case ItemFilter.IsFavoriteOrLikes:
+ query.IsFavoriteOrLiked = true;
+ break;
+ case ItemFilter.IsFolder:
+ query.IsFolder = true;
+ break;
+ case ItemFilter.IsNotFolder:
+ query.IsFolder = false;
+ break;
+ case ItemFilter.IsPlayed:
+ query.IsPlayed = true;
+ break;
+ case ItemFilter.IsResumable:
+ query.IsResumable = true;
+ break;
+ case ItemFilter.IsUnplayed:
+ query.IsPlayed = false;
+ break;
+ case ItemFilter.Likes:
+ query.IsLiked = true;
+ break;
+ }
+ }
- }, CancellationToken.None).ConfigureAwait(false);
+ var result = await _channelManager.GetChannelItems(query, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Get(GetLatestChannelItems request)
{
- var result = await _channelManager.GetLatestChannelItems(new AllChannelMediaQuery
+ var user = request.UserId.Equals(Guid.Empty)
+ ? null
+ : _userManager.GetUserById(request.UserId);
+
+ var query = new InternalItemsQuery(user)
{
Limit = request.Limit,
StartIndex = request.StartIndex,
- ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
- UserId = request.UserId,
- Filters = request.GetFilters().ToArray(),
- Fields = request.GetItemFields()
+ ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToArray(),
+ DtoOptions = new Controller.Dto.DtoOptions
+ {
+ Fields = request.GetItemFields()
+ }
+ };
+
+ foreach (var filter in request.GetFilters())
+ {
+ switch (filter)
+ {
+ case ItemFilter.Dislikes:
+ query.IsLiked = false;
+ break;
+ case ItemFilter.IsFavorite:
+ query.IsFavorite = true;
+ break;
+ case ItemFilter.IsFavoriteOrLikes:
+ query.IsFavoriteOrLiked = true;
+ break;
+ case ItemFilter.IsFolder:
+ query.IsFolder = true;
+ break;
+ case ItemFilter.IsNotFolder:
+ query.IsFolder = false;
+ break;
+ case ItemFilter.IsPlayed:
+ query.IsPlayed = true;
+ break;
+ case ItemFilter.IsResumable:
+ query.IsResumable = true;
+ break;
+ case ItemFilter.IsUnplayed:
+ query.IsPlayed = false;
+ break;
+ case ItemFilter.Likes:
+ query.IsLiked = true;
+ break;
+ }
+ }
- }, CancellationToken.None).ConfigureAwait(false);
+ var result = await _channelManager.GetLatestChannelItems(query, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 0023c13d7..9d14558e3 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -4,14 +4,11 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
-using System.Collections.Generic;
using System.IO;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Services;
+using System.Threading.Tasks;
namespace MediaBrowser.Api
{
@@ -59,27 +56,13 @@ namespace MediaBrowser.Api
}
- [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")]
- [Authenticated(Roles = "Admin")]
- public class GetMetadataPlugins : IReturn<MetadataPluginSummary[]>
- {
-
- }
-
- [Route("/System/Configuration/MetadataPlugins/Autoset", "POST")]
- [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
- public class AutoSetMetadataOptions : IReturnVoid
- {
-
- }
-
[Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")]
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class UpdateMediaEncoderPath : IReturnVoid
{
- [ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Path { get; set; }
- [ApiMember(Name = "PathType", Description = "PathType", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "PathType", Description = "PathType", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string PathType { get; set; }
}
@@ -132,10 +115,6 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
- public void Post(AutoSetMetadataOptions request)
- {
- }
-
/// <summary>
/// Posts the specified configuraiton.
/// </summary>
@@ -150,24 +129,19 @@ namespace MediaBrowser.Api
_configurationManager.ReplaceConfiguration(config);
}
- public void Post(UpdateNamedConfiguration request)
+ public async Task Post(UpdateNamedConfiguration request)
{
var key = GetPathValue(2);
var configurationType = _configurationManager.GetConfigurationType(key);
- var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType);
+ var configuration = await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, configurationType).ConfigureAwait(false);
_configurationManager.SaveConfiguration(key, configuration);
}
public object Get(GetDefaultMetadataOptions request)
{
- return ToOptimizedSerializedResultUsingCache(new MetadataOptions());
- }
-
- public object Get(GetMetadataPlugins request)
- {
- return ToOptimizedSerializedResultUsingCache(_providerManager.GetAllMetadataPlugins());
+ return ToOptimizedResult(new MetadataOptions());
}
}
}
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index c375e272a..9cc16b4a1 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -7,6 +7,8 @@ using MediaBrowser.Model.Session;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Controller.Session;
namespace MediaBrowser.Api.Devices
{
@@ -16,6 +18,22 @@ namespace MediaBrowser.Api.Devices
{
}
+ [Route("/Devices/Info", "GET", Summary = "Gets info for a device")]
+ [Authenticated(Roles = "Admin")]
+ public class GetDeviceInfo : IReturn<DeviceInfo>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Devices/Options", "GET", Summary = "Gets options for a device")]
+ [Authenticated(Roles = "Admin")]
+ public class GetDeviceOptions : IReturn<DeviceOptions>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
[Route("/Devices", "DELETE", Summary = "Deletes a device")]
public class DeleteDevice
{
@@ -35,37 +53,21 @@ namespace MediaBrowser.Api.Devices
[Authenticated]
public class PostCameraUpload : IRequiresRequestStream, IReturnVoid
{
- [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string DeviceId { get; set; }
- [ApiMember(Name = "Album", Description = "Album", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Album", Description = "Album", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Album { get; set; }
- [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Name { get; set; }
- [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
public Stream RequestStream { get; set; }
}
- [Route("/Devices/Info", "GET", Summary = "Gets device info")]
- [Authenticated]
- public class GetDeviceInfo : IReturn<DeviceInfo>
- {
- [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Devices/Capabilities", "GET", Summary = "Gets device capabilities")]
- [Authenticated]
- public class GetDeviceCapabilities : IReturn<ClientCapabilities>
- {
- [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
[Route("/Devices/Options", "POST", Summary = "Updates device options")]
[Authenticated(Roles = "Admin")]
public class PostDeviceOptions : DeviceOptions, IReturnVoid
@@ -77,34 +79,34 @@ namespace MediaBrowser.Api.Devices
public class DeviceService : BaseApiService
{
private readonly IDeviceManager _deviceManager;
+ private readonly IAuthenticationRepository _authRepo;
+ private readonly ISessionManager _sessionManager;
- public DeviceService(IDeviceManager deviceManager)
+ public DeviceService(IDeviceManager deviceManager, IAuthenticationRepository authRepo, ISessionManager sessionManager)
{
_deviceManager = deviceManager;
+ _authRepo = authRepo;
+ _sessionManager = sessionManager;
}
public void Post(PostDeviceOptions request)
{
- _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions
- {
- CustomName = request.CustomName,
- CameraUploadPath = request.CameraUploadPath
- });
+ _deviceManager.UpdateDeviceOptions(request.Id, request);
}
- public object Get(GetDeviceInfo request)
+ public object Get(GetDevices request)
{
- return ToOptimizedResult(_deviceManager.GetDevice(request.Id));
+ return ToOptimizedResult(_deviceManager.GetDevices(request));
}
- public object Get(GetDeviceCapabilities request)
+ public object Get(GetDeviceInfo request)
{
- return ToOptimizedResult(_deviceManager.GetCapabilities(request.Id));
+ return _deviceManager.GetDevice(request.Id);
}
- public object Get(GetDevices request)
+ public object Get(GetDeviceOptions request)
{
- return ToOptimizedResult(_deviceManager.GetDevices(request));
+ return _deviceManager.GetDeviceOptions(request.Id);
}
public object Get(GetCameraUploads request)
@@ -114,10 +116,19 @@ namespace MediaBrowser.Api.Devices
public void Delete(DeleteDevice request)
{
- _deviceManager.DeleteDevice(request.Id);
+ var sessions = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ DeviceId = request.Id
+
+ }).Items;
+
+ foreach (var session in sessions)
+ {
+ _sessionManager.Logout(session);
+ }
}
- public void Post(PostCameraUpload request)
+ public Task Post(PostCameraUpload request)
{
var deviceId = Request.QueryString["DeviceId"];
var album = Request.QueryString["Album"];
@@ -126,29 +137,25 @@ namespace MediaBrowser.Api.Devices
if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1)
{
- var task = _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
+ return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
{
MimeType = Request.ContentType,
Album = album,
Name = name,
Id = id
});
-
- Task.WaitAll(task);
}
else
{
var file = Request.Files.Length == 0 ? null : Request.Files[0];
- var task = _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
+ return _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
{
MimeType = file.ContentType,
Album = album,
Name = name,
Id = id
});
-
- Task.WaitAll(task);
}
}
}
diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs
index 4f8cc5255..6bc2f7e4a 100644
--- a/MediaBrowser.Api/DisplayPreferencesService.cs
+++ b/MediaBrowser.Api/DisplayPreferencesService.cs
@@ -76,7 +76,7 @@ namespace MediaBrowser.Api
{
var result = _displayPreferencesManager.GetDisplayPreferences(request.Id, request.UserId, request.Client);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index 400169ac5..ea3920d38 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -36,18 +36,6 @@ namespace MediaBrowser.Api
/// <value><c>true</c> if [include directories]; otherwise, <c>false</c>.</value>
[ApiMember(Name = "IncludeDirectories", Description = "An optional filter to include or exclude folders from the results. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool IncludeDirectories { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [include hidden].
- /// </summary>
- /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
- [ApiMember(Name = "IncludeHidden", Description = "An optional filter to include or exclude hidden files and folders. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
- public bool IncludeHidden { get; set; }
-
- public GetDirectoryContents()
- {
- IncludeHidden = true;
- }
}
[Route("/Environment/ValidatePath", "POST", Summary = "Gets the contents of a given directory in the file system")]
@@ -189,18 +177,7 @@ namespace MediaBrowser.Api
{
var result = new DefaultDirectoryBrowserInfo();
- try
- {
- var qnap = "/share/CACHEDEV1_DATA";
- if (_fileSystem.DirectoryExists(qnap))
- {
- result.Path = qnap;
- }
- }
- catch
- {
-
- }
+ result.Path = _fileSystem.DefaultDirectory;
return ToOptimizedResult(result);
}
@@ -223,10 +200,10 @@ namespace MediaBrowser.Api
if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf(UncSeparator) == 1)
{
- return ToOptimizedSerializedResultUsingCache(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
+ return ToOptimizedResult(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
}
- return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).ToList());
+ return ToOptimizedResult(GetFileSystemEntries(request).ToList());
}
public object Get(GetNetworkShares request)
@@ -235,7 +212,7 @@ namespace MediaBrowser.Api
var shares = GetNetworkShares(path).OrderBy(i => i.Path).ToList();
- return ToOptimizedSerializedResultUsingCache(shares);
+ return ToOptimizedResult(shares);
}
/// <summary>
@@ -247,7 +224,7 @@ namespace MediaBrowser.Api
{
var result = GetDrives().ToList();
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -273,7 +250,7 @@ namespace MediaBrowser.Api
{
var result = _networkManager.GetNetworkDevices().ToList();
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -300,11 +277,6 @@ namespace MediaBrowser.Api
{
var entries = _fileSystem.GetFileSystemEntries(request.Path).OrderBy(i => i.FullName).Where(i =>
{
- if (!request.IncludeHidden && i.IsHidden)
- {
- return false;
- }
-
var isDirectory = i.IsDirectory;
if (!request.IncludeFiles && !isDirectory)
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index 585e9c49b..59e203b7f 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
@@ -49,7 +49,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
@@ -76,6 +76,7 @@ namespace MediaBrowser.Api
public bool? IsKids { get; set; }
public bool? IsNews { get; set; }
public bool? IsSeries { get; set; }
+ public bool? Recursive { get; set; }
}
[Authenticated]
@@ -93,7 +94,7 @@ namespace MediaBrowser.Api
public object Get(GetQueryFilters request)
{
var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
@@ -107,7 +108,6 @@ namespace MediaBrowser.Api
var genreQuery = new InternalItemsQuery(user)
{
- AncestorIds = parentItem == null ? new string[] { } : new string[] { parentItem.Id.ToString("N") },
IncludeItemTypes = request.GetIncludeItemTypes(),
DtoOptions = new Controller.Dto.DtoOptions
{
@@ -123,34 +123,44 @@ namespace MediaBrowser.Api
IsSeries = request.IsSeries
};
+ // Non recursive not yet supported for library folders
+ if ((request.Recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder)
+ {
+ genreQuery.AncestorIds = parentItem == null ? Array.Empty<Guid>() : new Guid[] { parentItem.Id };
+ }
+ else
+ {
+ genreQuery.Parent = parentItem;
+ }
+
if (string.Equals(request.IncludeItemTypes, "MusicAlbum", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "MusicVideo", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "MusicArtist", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Audio", StringComparison.OrdinalIgnoreCase))
{
- filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameIdPair
+ filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
- Id = i.Item1.Id.ToString("N")
+ Id = i.Item1.Id
}).ToArray();
}
else if (string.Equals(request.IncludeItemTypes, "Game", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "GameSystem", StringComparison.OrdinalIgnoreCase))
{
- filters.Genres = _libraryManager.GetGameGenres(genreQuery).Items.Select(i => new NameIdPair
+ filters.Genres = _libraryManager.GetGameGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
- Id = i.Item1.Id.ToString("N")
+ Id = i.Item1.Id
}).ToArray();
}
else
{
- filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameIdPair
+ filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
- Id = i.Item1.Id.ToString("N")
+ Id = i.Item1.Id
}).ToArray();
}
@@ -161,7 +171,7 @@ namespace MediaBrowser.Api
public object Get(GetQueryFiltersLegacy request)
{
var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
@@ -172,7 +182,7 @@ namespace MediaBrowser.Api
}
var item = string.IsNullOrEmpty(request.ParentId) ?
- user == null ? _libraryManager.RootFolder : user.RootFolder :
+ user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder() :
parentItem;
var result = ((Folder)item).GetItemList(GetItemsQuery(request, user));
diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs
index 6c48b732f..3e4e20506 100644
--- a/MediaBrowser.Api/GamesService.cs
+++ b/MediaBrowser.Api/GamesService.cs
@@ -17,14 +17,6 @@ using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetSimilarGames
- /// </summary>
- [Route("/Games/{Id}/Similar", "GET", Summary = "Finds games similar to a given game.")]
- public class GetSimilarGames : BaseGetSimilarItemsFromItem
- {
- }
-
- /// <summary>
/// Class GetGameSystemSummaries
/// </summary>
[Route("/Games/SystemSummaries", "GET", Summary = "Finds games similar to a given game.")]
@@ -35,7 +27,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -109,11 +101,9 @@ namespace MediaBrowser.Api
.Select(i => GetSummary(i, user))
.ToArray();
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
/// <summary>
/// Gets the summary.
/// </summary>
@@ -151,51 +141,5 @@ namespace MediaBrowser.Api
return summary;
}
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetSimilarGames request)
- {
- var result = GetSimilarItemsResult(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request)
- {
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-
- var item = string.IsNullOrEmpty(request.Id) ?
- (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
- _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
-
- var dtoOptions = GetDtoOptions(_authContext, request);
-
- var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- Limit = request.Limit,
- IncludeItemTypes = new[]
- {
- typeof(Game).Name
- },
- SimilarTo = item,
- DtoOptions = dtoOptions
-
- });
-
- var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnList,
-
- TotalRecordCount = itemsResult.Count
- };
-
- return result;
- }
}
}
diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs
index b61c81972..d4c01fdb0 100644
--- a/MediaBrowser.Api/Images/ImageRequest.cs
+++ b/MediaBrowser.Api/Images/ImageRequest.cs
@@ -87,18 +87,14 @@ namespace MediaBrowser.Api.Images
/// Gets or sets the type of the image.
/// </summary>
/// <value>The type of the image.</value>
- [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+ [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET,POST,DELETE")]
public ImageType Type { get; set; }
/// <summary>
/// Gets or sets the index.
/// </summary>
/// <value>The index.</value>
- [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")]
+ [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET,POST,DELETE")]
public int? Index { get; set; }
}
}
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 2b8ac1a66..c3b2e82e7 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -50,8 +50,8 @@ namespace MediaBrowser.Api.Images
/// 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 = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path")]
+ public Guid Id { get; set; }
}
/// <summary>
@@ -65,7 +65,7 @@ namespace MediaBrowser.Api.Images
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
@@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Images
/// Gets or sets the new index.
/// </summary>
/// <value>The new index.</value>
- [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public int NewIndex { get; set; }
}
@@ -105,8 +105,8 @@ namespace MediaBrowser.Api.Images
[Route("/Persons/{Name}/Images/{Type}/{Index}", "GET")]
[Route("/Studios/{Name}/Images/{Type}", "GET")]
[Route("/Studios/{Name}/Images/{Type}/{Index}", "GET")]
- [Route("/Years/{Year}/Images/{Type}", "GET")]
- [Route("/Years/{Year}/Images/{Type}/{Index}", "GET")]
+ ////[Route("/Years/{Year}/Images/{Type}", "GET")]
+ ////[Route("/Years/{Year}/Images/{Type}/{Index}", "GET")]
[Route("/Artists/{Name}/Images/{Type}", "HEAD")]
[Route("/Artists/{Name}/Images/{Type}/{Index}", "HEAD")]
[Route("/Genres/{Name}/Images/{Type}", "HEAD")]
@@ -119,8 +119,8 @@ namespace MediaBrowser.Api.Images
[Route("/Persons/{Name}/Images/{Type}/{Index}", "HEAD")]
[Route("/Studios/{Name}/Images/{Type}", "HEAD")]
[Route("/Studios/{Name}/Images/{Type}/{Index}", "HEAD")]
- [Route("/Years/{Year}/Images/{Type}", "HEAD")]
- [Route("/Years/{Year}/Images/{Type}/{Index}", "HEAD")]
+ ////[Route("/Years/{Year}/Images/{Type}", "HEAD")]
+ ////[Route("/Years/{Year}/Images/{Type}/{Index}", "HEAD")]
public class GetItemByNameImage : ImageRequest
{
/// <summary>
@@ -145,7 +145,7 @@ namespace MediaBrowser.Api.Images
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -177,7 +177,7 @@ namespace MediaBrowser.Api.Images
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -265,7 +265,7 @@ namespace MediaBrowser.Api.Images
var result = GetItemImageInfos(item);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -373,11 +373,7 @@ namespace MediaBrowser.Api.Images
/// <returns>System.Object.</returns>
public object Get(GetItemImage request)
{
- var item = string.IsNullOrEmpty(request.Id) ?
- _libraryManager.RootFolder :
- _libraryManager.GetItemById(request.Id);
-
- return GetImage(request, item, false);
+ return GetImage(request, request.Id, null, false);
}
/// <summary>
@@ -387,11 +383,7 @@ namespace MediaBrowser.Api.Images
/// <returns>System.Object.</returns>
public object Head(GetItemImage request)
{
- var item = string.IsNullOrEmpty(request.Id) ?
- _libraryManager.RootFolder :
- _libraryManager.GetItemById(request.Id);
-
- return GetImage(request, item, true);
+ return GetImage(request, request.Id, null, true);
}
/// <summary>
@@ -403,14 +395,14 @@ namespace MediaBrowser.Api.Images
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, item, false);
+ return GetImage(request, Guid.Empty, item, false);
}
public object Head(GetUserImage request)
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, item, true);
+ return GetImage(request, Guid.Empty, item, true);
}
public object Get(GetItemByNameImage request)
@@ -419,7 +411,7 @@ namespace MediaBrowser.Api.Images
var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false));
- return GetImage(request, item, false);
+ return GetImage(request, item.Id, item, false);
}
public object Head(GetItemByNameImage request)
@@ -428,32 +420,30 @@ namespace MediaBrowser.Api.Images
var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false));
- return GetImage(request, item, true);
+ return GetImage(request, item.Id, item, true);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(PostUserImage request)
+ public Task Post(PostUserImage request)
{
var userId = GetPathValue(1);
- AssertCanUpdateUser(_authContext, _userManager, userId, true);
+ AssertCanUpdateUser(_authContext, _userManager, new Guid(userId), true);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
var item = _userManager.GetUserById(userId);
- var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType);
-
- Task.WaitAll(task);
+ return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(PostItemImage request)
+ public Task Post(PostItemImage request)
{
var id = GetPathValue(1);
@@ -461,9 +451,7 @@ namespace MediaBrowser.Api.Images
var item = _libraryManager.GetItemById(id);
- var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType);
-
- Task.WaitAll(task);
+ return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
}
/// <summary>
@@ -510,7 +498,7 @@ namespace MediaBrowser.Api.Images
/// <param name="currentIndex">Index of the current.</param>
/// <param name="newIndex">The new index.</param>
/// <returns>Task.</returns>
- private void UpdateItemIndex(IHasMetadata item, ImageType type, int currentIndex, int newIndex)
+ private void UpdateItemIndex(BaseItem item, ImageType type, int currentIndex, int newIndex)
{
item.SwapImages(type, currentIndex, newIndex);
}
@@ -523,7 +511,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, IHasMetadata item, bool isHeadRequest)
+ public Task<object> GetImage(ImageRequest request, Guid itemId, BaseItem item, bool isHeadRequest)
{
if (request.PercentPlayed.HasValue)
{
@@ -549,18 +537,42 @@ namespace MediaBrowser.Api.Images
}
}
+ if (item == null)
+ {
+ item = _libraryManager.GetItemById(itemId);
+
+ if (item == null)
+ {
+ throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N")));
+ }
+ }
+
var imageInfo = GetImageInfo(request, item);
if (imageInfo == null)
{
- throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
+ var displayText = item == null ? itemId.ToString() : item.Name;
+ throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type));
}
- var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : new List<IImageEnhancer>();
+ IImageEnhancer[] supportedImageEnhancers;
+
+ if (_imageProcessor.ImageEnhancers.Length > 0)
+ {
+ if (item == null)
+ {
+ item = _libraryManager.GetItemById(itemId);
+ }
+
+ supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : Array.Empty<IImageEnhancer>();
+ }
+ else
+ {
+ supportedImageEnhancers = Array.Empty<IImageEnhancer>();
+ }
var cropwhitespace = request.Type == ImageType.Logo ||
- request.Type == ImageType.Art
- || (request.Type == ImageType.Primary && item is LiveTvChannel);
+ request.Type == ImageType.Art;
if (request.CropWhitespace.HasValue)
{
@@ -583,6 +595,7 @@ namespace MediaBrowser.Api.Images
};
return GetImageResult(item,
+ itemId,
request,
imageInfo,
cropwhitespace,
@@ -593,12 +606,13 @@ namespace MediaBrowser.Api.Images
isHeadRequest);
}
- private async Task<object> GetImageResult(IHasMetadata item,
+ private async Task<object> GetImageResult(BaseItem item,
+ Guid itemId,
ImageRequest request,
ItemImageInfo image,
bool cropwhitespace,
ImageFormat[] supportedFormats,
- List<IImageEnhancer> enhancers,
+ IImageEnhancer[] enhancers,
TimeSpan? cacheDuration,
IDictionary<string, string> headers,
bool isHeadRequest)
@@ -611,8 +625,7 @@ namespace MediaBrowser.Api.Images
ImageIndex = request.Index ?? 0,
Image = image,
Item = item,
- ItemId = item.Id.ToString("N"),
- ItemType = item.GetType().Name,
+ ItemId = itemId,
MaxHeight = request.MaxHeight,
MaxWidth = request.MaxWidth,
Quality = request.Quality ?? 100,
@@ -660,21 +673,15 @@ namespace MediaBrowser.Api.Images
private ImageFormat[] GetClientSupportedFormats()
{
- //Logger.Debug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? new string[] { }));
- var supportsWebP = (Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
+ //Logger.Debug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? Array.Empty<string>()));
+ var supportedFormats = (Request.AcceptTypes ?? Array.Empty<string>()).Select(i => i.Split(';')[0]).ToArray();
+ var acceptParam = Request.QueryString["accept"];
- var userAgent = Request.UserAgent ?? string.Empty;
-
- if (!supportsWebP)
- {
- if (string.Equals(Request.QueryString["accept"], "webp", StringComparison.OrdinalIgnoreCase))
- {
- supportsWebP = true;
- }
- }
+ var supportsWebP = SupportsFormat(supportedFormats, acceptParam, "webp", false);
if (!supportsWebP)
{
+ var userAgent = Request.UserAgent ?? string.Empty;
if (userAgent.IndexOf("crosswalk", StringComparison.OrdinalIgnoreCase) != -1 &&
userAgent.IndexOf("android", StringComparison.OrdinalIgnoreCase) != -1)
{
@@ -682,16 +689,39 @@ namespace MediaBrowser.Api.Images
}
}
+ var formats = new List<ImageFormat>(4);
+
if (supportsWebP)
{
- // Not displaying properly on iOS
- if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1)
- {
- return new[] { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
- }
+ formats.Add(ImageFormat.Webp);
+ }
+
+ formats.Add(ImageFormat.Jpg);
+ formats.Add(ImageFormat.Png);
+
+ if (SupportsFormat(supportedFormats, acceptParam, "gif", true))
+ {
+ formats.Add(ImageFormat.Gif);
+ }
+
+ return formats.ToArray();
+ }
+
+ private bool SupportsFormat(string[] requestAcceptTypes, string acceptParam, string format, bool acceptAll)
+ {
+ var mimeType = "image/" + format;
+
+ if (requestAcceptTypes.Contains(mimeType))
+ {
+ return true;
+ }
+
+ if (acceptAll && requestAcceptTypes.Contains("*/*"))
+ {
+ return true;
}
- return new[] { ImageFormat.Jpg, ImageFormat.Png };
+ return string.Equals(Request.QueryString["accept"], format, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -700,7 +730,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, IHasMetadata item)
+ private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
{
var index = request.Index ?? 0;
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index 4782d76df..8d75ec10c 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -73,13 +73,13 @@ namespace MediaBrowser.Api.Images
public class BaseDownloadRemoteImage : IReturnVoid
{
- [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public ImageType Type { get; set; }
- [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string ProviderName { get; set; }
- [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string ImageUrl { get; set; }
}
@@ -91,7 +91,7 @@ namespace MediaBrowser.Api.Images
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
@@ -129,7 +129,7 @@ namespace MediaBrowser.Api.Images
var result = GetImageProviders(item);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
private List<ImageProviderInfo> GetImageProviders(BaseItem item)
@@ -188,13 +188,11 @@ namespace MediaBrowser.Api.Images
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(DownloadRemoteImage request)
+ public Task Post(DownloadRemoteImage request)
{
var item = _libraryManager.GetItemById(request.Id);
- var task = DownloadRemoteImage(item, request);
-
- Task.WaitAll(task);
+ return DownloadRemoteImage(item, request);
}
/// <summary>
@@ -215,12 +213,7 @@ namespace MediaBrowser.Api.Images
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetRemoteImage request)
- {
- return GetAsync(request).Result;
- }
-
- public async Task<object> GetAsync(GetRemoteImage request)
+ public async Task<object> Get(GetRemoteImage request)
{
var urlHash = request.ImageUrl.GetMD5();
var pointerCachePath = GetFullCachePath(urlHash.ToString());
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index a454642a4..39a790486 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
[Route("/Items/RemoteSearch/Movie", "POST")]
@@ -44,9 +44,9 @@ namespace MediaBrowser.Api
{
}
- [Route("/Items/RemoteSearch/AdultVideo", "POST")]
+ [Route("/Items/RemoteSearch/MusicVideo", "POST")]
[Authenticated]
- public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
+ public class GetMusicVideoRemoteSearchResults : RemoteSearchQuery<MusicVideoInfo>, IReturn<List<RemoteSearchResult>>
{
}
@@ -186,6 +186,13 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
+ public async Task<object> Post(GetMusicVideoRemoteSearchResults request)
+ {
+ var result = await _providerManager.GetRemoteSearchResults<MusicVideo, MusicVideoInfo>(request, CancellationToken.None).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
public async Task<object> Post(GetPersonRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Person, PersonLookupInfo>(request, CancellationToken.None).ConfigureAwait(false);
@@ -212,7 +219,7 @@ namespace MediaBrowser.Api
return GetRemoteImage(request);
}
- public void Post(ApplySearchCriteria request)
+ public Task Post(ApplySearchCriteria request)
{
var item = _libraryManager.GetItemById(new Guid(request.Id));
@@ -232,17 +239,15 @@ namespace MediaBrowser.Api
//item.ProductionYear = request.ProductionYear;
//item.Name = request.Name;
- var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)
+ return _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)
{
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ImageRefreshMode = ImageRefreshMode.FullRefresh,
+ ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllMetadata = true,
ReplaceAllImages = request.ReplaceAllImages,
- SearchResult = request,
- ForceEnableInternetMetadata = true
+ SearchResult = request
}, CancellationToken.None);
- Task.WaitAll(task);
}
/// <summary>
diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs
index d26fb768a..ab083c207 100644
--- a/MediaBrowser.Api/ItemRefreshService.cs
+++ b/MediaBrowser.Api/ItemRefreshService.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Api
public MetadataRefreshMode MetadataRefreshMode { get; set; }
[ApiMember(Name = "ImageRefreshMode", Description = "Specifies the image refresh mode", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
- public ImageRefreshMode ImageRefreshMode { get; set; }
+ public MetadataRefreshMode ImageRefreshMode { get; set; }
[ApiMember(Name = "ReplaceAllMetadata", Description = "Determines if metadata should be replaced. Only applicable if mode is FullRefresh", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool ReplaceAllMetadata { get; set; }
@@ -73,9 +73,8 @@ namespace MediaBrowser.Api
ImageRefreshMode = request.ImageRefreshMode,
ReplaceAllImages = request.ReplaceAllImages,
ReplaceAllMetadata = request.ReplaceAllMetadata,
- ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == ImageRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata,
- IsAutomated = false,
- ValidateChildren = request.Recursive
+ ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == MetadataRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata,
+ IsAutomated = false
};
}
}
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index a55741d7d..22eb7ea09 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -15,6 +15,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Api
{
@@ -36,7 +37,7 @@ namespace MediaBrowser.Api
public class UpdateItemContentType : IReturnVoid
{
[ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string ItemId { get; set; }
+ public Guid ItemId { get; set; }
[ApiMember(Name = "ContentType", Description = "The content type of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ContentType { get; set; }
@@ -49,13 +50,15 @@ namespace MediaBrowser.Api
private readonly IProviderManager _providerManager;
private readonly ILocalizationManager _localizationManager;
private readonly IServerConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
- public ItemUpdateService(ILibraryManager libraryManager, IProviderManager providerManager, ILocalizationManager localizationManager, IServerConfigurationManager config)
+ public ItemUpdateService(IFileSystem fileSystem, ILibraryManager libraryManager, IProviderManager providerManager, ILocalizationManager localizationManager, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
_providerManager = providerManager;
_localizationManager = localizationManager;
_config = config;
+ _fileSystem = fileSystem;
}
public object Get(GetMetadataEditorInfo request)
@@ -199,6 +202,9 @@ namespace MediaBrowser.Api
var newLockData = request.LockData ?? false;
var isLockedChanged = item.IsLocked != newLockData;
+ var series = item as Series;
+ var displayOrderChanged = series != null && !string.Equals(series.DisplayOrder ?? string.Empty, request.DisplayOrder ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+
// Do this first so that metadata savers can pull the updates from the database.
if (request.People != null)
{
@@ -221,6 +227,17 @@ namespace MediaBrowser.Api
child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
}
}
+
+ if (displayOrderChanged)
+ {
+ _providerManager.QueueRefresh(series.Id, new MetadataRefreshOptions(_fileSystem)
+ {
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
+ ImageRefreshMode = MetadataRefreshMode.FullRefresh,
+ ReplaceAllMetadata = true
+
+ }, RefreshPriority.High);
+ }
}
private DateTime NormalizeDateTime(DateTime val)
@@ -238,7 +255,6 @@ namespace MediaBrowser.Api
item.CriticRating = request.CriticRating;
item.CommunityRating = request.CommunityRating;
- item.HomePageUrl = request.HomePageUrl;
item.IndexNumber = request.IndexNumber;
item.ParentIndexNumber = request.ParentIndexNumber;
item.Overview = request.Overview;
@@ -247,12 +263,9 @@ namespace MediaBrowser.Api
var episode = item as Episode;
if (episode != null)
{
- episode.DvdSeasonNumber = request.DvdSeasonNumber;
- episode.DvdEpisodeNumber = request.DvdEpisodeNumber;
episode.AirsAfterSeasonNumber = request.AirsAfterSeasonNumber;
episode.AirsBeforeEpisodeNumber = request.AirsBeforeEpisodeNumber;
episode.AirsBeforeSeasonNumber = request.AirsBeforeSeasonNumber;
- episode.AbsoluteEpisodeNumber = request.AbsoluteEpisodeNumber;
}
item.Tags = request.Tags;
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index a036a00a6..cc8c1251f 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -30,6 +30,8 @@ using MediaBrowser.Model.Services;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Api.Library
{
@@ -50,7 +52,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
[Route("/Items/{Id}/CriticReviews", "GET", Summary = "Gets critic reviews for an item")]
[Authenticated]
- public class GetCriticReviews : IReturn<QueryResult<ItemReview>>
+ public class GetCriticReviews : IReturn<QueryResult<BaseItemDto>>
{
/// <summary>
/// Gets or sets the id.
@@ -86,7 +88,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -111,7 +113,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -136,7 +138,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -167,7 +169,7 @@ namespace MediaBrowser.Api.Library
[Authenticated]
public class DeleteItems : IReturnVoid
{
- [ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+ [ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string Ids { get; set; }
}
@@ -176,7 +178,7 @@ namespace MediaBrowser.Api.Library
public class GetItemCounts : IReturn<ItemCounts>
{
[ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[ApiMember(Name = "IsFavorite", Description = "Optional. Get counts of favorite items", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsFavorite { get; set; }
@@ -191,7 +193,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -201,21 +203,6 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
- [Route("/Items/YearIndex", "GET", Summary = "Gets a year index based on an item query.")]
- [Authenticated]
- public class GetYearIndex : IReturn<List<ItemIndex>>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
-
- [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string IncludeItemTypes { get; set; }
- }
-
/// <summary>
/// Class GetPhyscialPaths
/// </summary>
@@ -238,7 +225,7 @@ namespace MediaBrowser.Api.Library
[Authenticated]
public class PostUpdatedSeries : IReturnVoid
{
- [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")]
public string TvdbId { get; set; }
}
@@ -247,12 +234,28 @@ namespace MediaBrowser.Api.Library
[Authenticated]
public class PostUpdatedMovies : IReturnVoid
{
- [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")]
public string TmdbId { get; set; }
- [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")]
public string ImdbId { get; set; }
}
+ public class MediaUpdateInfo
+ {
+ public string Path { get; set; }
+
+ // Created, Modified, Deleted
+ public string UpdateType { get; set; }
+ }
+
+ [Route("/Library/Media/Updated", "POST", Summary = "Reports that new movies have been added by an external source")]
+ [Authenticated]
+ public class PostUpdatedMedia : IReturnVoid
+ {
+ [ApiMember(Name = "Updates", Description = "A list of updated media paths", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")]
+ public List<MediaUpdateInfo> Updates { get; set; }
+ }
+
[Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")]
[Authenticated(Roles = "download")]
public class GetDownload
@@ -265,12 +268,49 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
+ [Route("/Games/{Id}/Similar", "GET", Summary = "Finds games similar to a given game.")]
+ [Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
[Route("/Items/{Id}/Similar", "GET", Summary = "Gets similar items")]
+ [Route("/Albums/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
+ [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
+ [Route("/Movies/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given movie.")]
+ [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
[Authenticated]
public class GetSimilarItems : BaseGetSimilarItemsFromItem
{
}
+ [Route("/Libraries/AvailableOptions", "GET")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class GetLibraryOptionsInfo : IReturn<LibraryOptionsResult>
+ {
+ public string LibraryContentType { get; set; }
+ public bool IsNewLibrary { get; set; }
+ }
+
+ public class LibraryOptionInfo
+ {
+ public string Name { get; set; }
+ public bool DefaultEnabled { get; set; }
+ }
+
+ public class LibraryOptionsResult
+ {
+ public LibraryOptionInfo[] MetadataSavers { get; set; }
+ public LibraryOptionInfo[] MetadataReaders { get; set; }
+ public LibraryOptionInfo[] SubtitleFetchers { get; set; }
+ public LibraryTypeOptions[] TypeOptions { get; set; }
+ }
+
+ public class LibraryTypeOptions
+ {
+ public string Type { get; set; }
+ public LibraryOptionInfo[] MetadataFetchers { get; set; }
+ public LibraryOptionInfo[] ImageFetchers { get; set; }
+ public ImageType[] SupportedImageTypes { get; set; }
+ public ImageOption[] DefaultImageOptions { get; set; }
+ }
+
/// <summary>
/// Class LibraryService
/// </summary>
@@ -294,11 +334,12 @@ namespace MediaBrowser.Api.Library
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config;
+ private readonly IProviderManager _providerManager;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary>
- public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
+ public LibraryService(IProviderManager providerManager, IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, IServerConfigurationManager config)
{
_itemRepo = itemRepo;
@@ -314,63 +355,287 @@ namespace MediaBrowser.Api.Library
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_config = config;
+ _providerManager = providerManager;
}
- public object Get(GetSimilarItems request)
+ private string[] GetRepresentativeItemTypes(string contentType)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ switch (contentType)
+ {
+ case CollectionType.BoxSets:
+ return new string[] { "BoxSet" };
+ case CollectionType.Playlists:
+ return new string[] { "Playlist" };
+ case CollectionType.Movies:
+ return new string[] { "Movie" };
+ case CollectionType.TvShows:
+ return new string[] { "Series", "Season", "Episode" };
+ case CollectionType.Books:
+ return new string[] { "Book" };
+ case CollectionType.Games:
+ return new string[] { "Game", "GameSystem" };
+ case CollectionType.Music:
+ return new string[] { "MusicAlbum", "MusicArtist", "Audio", "MusicVideo" };
+ case CollectionType.HomeVideos:
+ case CollectionType.Photos:
+ return new string[] { "Video", "Photo" };
+ case CollectionType.MusicVideos:
+ return new string[] { "MusicVideo" };
+ default:
+ return new string[] { "Series", "Season", "Episode", "Movie" };
+ }
+ }
- var item = string.IsNullOrEmpty(request.Id) ?
- (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
- _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+ private bool IsSaverEnabledByDefault(string name, string[] itemTypes, bool isNewLibrary)
+ {
+ if (isNewLibrary)
+ {
+ return false;
+ }
- if (item is Game)
+ var metadataOptions = _config.Configuration.MetadataOptions
+ .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ .ToArray();
+
+ if (metadataOptions.Length == 0)
{
- return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
- {
- Request = Request,
+ return true;
+ }
- }.Get(new GetSimilarGames
+ return metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringComparer.OrdinalIgnoreCase));
+ }
+
+ private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
+ {
+ if (isNewLibrary)
+ {
+ if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
{
- Fields = request.Fields,
- Id = request.Id,
- Limit = request.Limit,
- UserId = request.UserId,
- ImageTypeLimit = request.ImageTypeLimit
- });
+ if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ return true;
+ }
+ else if (string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(name, "The Open Movie Database", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ else if (string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ var metadataOptions = _config.Configuration.MetadataOptions
+ .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+
+ if (metadataOptions.Length == 0)
+ {
+ return true;
}
- if (item is MusicAlbum)
+
+ return metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase));
+ }
+
+ private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
+ {
+ if (isNewLibrary)
{
- return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
+ if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
{
- Request = Request,
-
- }.Get(new GetSimilarAlbums
+ if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ return true;
+ }
+ else if (string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase))
{
- Fields = request.Fields,
- Id = request.Id,
- Limit = request.Limit,
- UserId = request.UserId,
- ExcludeArtistIds = request.ExcludeArtistIds,
- ImageTypeLimit = request.ImageTypeLimit
- });
+ return true;
+ }
+ else if (string.Equals(name, "The Open Movie Database", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ else if (string.Equals(name, "FanArt", StringComparison.OrdinalIgnoreCase))
+ {
+ if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ return true;
+ }
+ else if (string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(name, "Emby Designs", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return false;
}
- if (item is MusicArtist)
+
+ var metadataOptions = _config.Configuration.MetadataOptions
+ .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+
+ if (metadataOptions.Length == 0)
{
- return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
+ return true;
+ }
+
+ return metadataOptions.Any(i => !i.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase));
+ }
+
+ public object Get(GetLibraryOptionsInfo request)
+ {
+ var result = new LibraryOptionsResult();
+
+ var types = GetRepresentativeItemTypes(request.LibraryContentType);
+ var isNewLibrary = request.IsNewLibrary;
+ var typesList = types.ToList();
+
+ var plugins = _providerManager.GetAllMetadataPlugins()
+ .Where(i => types.Contains(i.ItemType, StringComparer.OrdinalIgnoreCase))
+ .OrderBy(i => typesList.IndexOf(i.ItemType))
+ .ToList();
+
+ result.MetadataSavers = plugins
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver))
+ .Select(i => new LibraryOptionInfo
{
- Request = Request,
+ Name = i.Name,
+ DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary)
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray();
- }.Get(new GetSimilarArtists
+ result.MetadataReaders = plugins
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalMetadataProvider))
+ .Select(i => new LibraryOptionInfo
{
- Fields = request.Fields,
- Id = request.Id,
- Limit = request.Limit,
- UserId = request.UserId,
- ImageTypeLimit = request.ImageTypeLimit
+ Name = i.Name,
+ DefaultEnabled = true
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ result.SubtitleFetchers = plugins
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.SubtitleFetcher))
+ .Select(i => new LibraryOptionInfo
+ {
+ Name = i.Name,
+ DefaultEnabled = true
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ var typeOptions = new List<LibraryTypeOptions>();
+
+ foreach (var type in types)
+ {
+ ImageOption[] defaultImageOptions = null;
+ TypeOptions.DefaultImageOptions.TryGetValue(type, out defaultImageOptions);
+
+ typeOptions.Add(new LibraryTypeOptions
+ {
+ Type = type,
+
+ MetadataFetchers = plugins
+ .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
+ .Select(i => new LibraryOptionInfo
+ {
+ Name = i.Name,
+ DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray(),
+
+ ImageFetchers = plugins
+ .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
+ .Select(i => new LibraryOptionInfo
+ {
+ Name = i.Name,
+ DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray(),
+
+ SupportedImageTypes = plugins
+ .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
+ .Distinct()
+ .ToArray(),
+
+ DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>()
});
}
+ result.TypeOptions = typeOptions.ToArray();
+
+ return result;
+ }
+
+ public object Get(GetSimilarItems request)
+ {
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
+
+ var item = string.IsNullOrEmpty(request.Id) ?
+ (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() :
+ _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+
var program = item as IHasProgramAttributes;
if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
@@ -379,33 +644,70 @@ namespace MediaBrowser.Api.Library
{
Request = Request,
- }.Get(new GetSimilarMovies
- {
- Fields = request.Fields,
- Id = request.Id,
- Limit = request.Limit,
- UserId = request.UserId,
- ImageTypeLimit = request.ImageTypeLimit
- });
+ }.GetSimilarItemsResult(request);
}
- if (item is Series || (program != null && program.IsSeries))
+ if (program != null && program.IsSeries)
{
- return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager, _authContext)
- {
- Request = Request,
+ return GetSimilarItemsResult(request, new[] { typeof(Series).Name });
+ }
- }.Get(new GetSimilarShows
- {
- Fields = request.Fields,
- Id = request.Id,
- Limit = request.Limit,
- UserId = request.UserId,
- ImageTypeLimit = request.ImageTypeLimit
- });
+ if (item is Episode || (item is IItemByName && !(item is MusicArtist)))
+ {
+ return new QueryResult<BaseItemDto>();
}
- return new QueryResult<BaseItemDto>();
+ return GetSimilarItemsResult(request, new[] { item.GetType().Name });
+ }
+
+ private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, string[] includeItemTypes)
+ {
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
+
+ var item = string.IsNullOrEmpty(request.Id) ?
+ (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() :
+ _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+
+ var dtoOptions = GetDtoOptions(_authContext, request);
+
+ var query = new InternalItemsQuery(user)
+ {
+ Limit = request.Limit,
+ IncludeItemTypes = includeItemTypes,
+ SimilarTo = item,
+ DtoOptions = dtoOptions,
+ EnableTotalRecordCount = false
+ };
+
+ // ExcludeArtistIds
+ if (!string.IsNullOrEmpty(request.ExcludeArtistIds))
+ {
+ query.ExcludeArtistIds = BaseApiService.GetGuids(request.ExcludeArtistIds);
+ }
+
+ List<BaseItem> itemsResult;
+
+ if (item is MusicArtist)
+ {
+ query.IncludeItemTypes = Array.Empty<string>();
+
+ itemsResult = _libraryManager.GetArtists(query).Items.Select(i => i.Item1).ToList();
+ }
+ else
+ {
+ itemsResult = _libraryManager.GetItemList(query);
+ }
+
+ var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
+
+ var result = new QueryResult<BaseItemDto>
+ {
+ Items = returnList,
+
+ TotalRecordCount = itemsResult.Count
+ };
+
+ return result;
}
public object Get(GetMediaFolders request)
@@ -428,7 +730,7 @@ namespace MediaBrowser.Api.Library
Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions)).ToArray()
};
- return ToOptimizedResult(result);
+ return result;
}
public void Post(PostUpdatedSeries request)
@@ -443,17 +745,21 @@ namespace MediaBrowser.Api.Library
}).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
- if (series.Length > 0)
+ foreach (var item in series)
{
- foreach (var item in series)
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
+ }
+ }
+
+ public void Post(PostUpdatedMedia request)
+ {
+ if (request.Updates != null)
+ {
+ foreach (var item in request.Updates)
{
_libraryMonitor.ReportFileSystemChanged(item.Path);
}
}
- else
- {
- Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None));
- }
}
public void Post(PostUpdatedMovies request)
@@ -481,16 +787,9 @@ namespace MediaBrowser.Api.Library
movies = new List<BaseItem>();
}
- if (movies.Count > 0)
- {
- foreach (var item in movies)
- {
- _libraryMonitor.ReportFileSystemChanged(item.Path);
- }
- }
- else
+ foreach (var item in movies)
{
- Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None));
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
}
}
@@ -499,7 +798,7 @@ namespace MediaBrowser.Api.Library
var item = _libraryManager.GetItemById(request.Id);
var auth = _authContext.GetAuthorizationInfo(Request);
- var user = _userManager.GetUserById(auth.UserId);
+ var user = auth.User;
if (user != null)
{
@@ -561,15 +860,6 @@ namespace MediaBrowser.Api.Library
public Task<object> Get(GetFile request)
{
var item = _libraryManager.GetItemById(request.Id);
- var locationType = item.LocationType;
- if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
- {
- throw new ArgumentException("This command cannot be used for remote or virtual items.");
- }
- if (_fileSystem.DirectoryExists(item.Path))
- {
- throw new ArgumentException("This command cannot be used for directories.");
- }
return ResultFactory.GetStaticFileResult(Request, item.Path);
}
@@ -585,7 +875,7 @@ namespace MediaBrowser.Api.Library
.SelectMany(c => c.PhysicalLocations)
.ToList();
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -597,7 +887,7 @@ namespace MediaBrowser.Api.Library
{
var result = GetAncestors(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -611,7 +901,7 @@ namespace MediaBrowser.Api.Library
var baseItemDtos = new List<BaseItemDto>();
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var dtoOptions = GetDtoOptions(_authContext, request);
@@ -636,7 +926,7 @@ namespace MediaBrowser.Api.Library
{
if (item.GetParent() is AggregateFolder)
{
- return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
+ return _libraryManager.GetUserRootFolder().GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
}
return item;
@@ -649,9 +939,7 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns>
public object Get(GetCriticReviews request)
{
- var result = GetCriticReviews(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
+ return new QueryResult<BaseItemDto>();
}
/// <summary>
@@ -661,7 +949,7 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns>
public object Get(GetItemCounts request)
{
- var user = string.IsNullOrWhiteSpace(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+ var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
var counts = new ItemCounts
{
@@ -677,7 +965,7 @@ namespace MediaBrowser.Api.Library
BookCount = GetCount(typeof(Book), user, request)
};
- return ToOptimizedSerializedResultUsingCache(counts);
+ return ToOptimizedResult(counts);
}
private int GetCount(Type type, User user, GetItemCounts request)
@@ -724,14 +1012,14 @@ namespace MediaBrowser.Api.Library
public void Delete(DeleteItems request)
{
var ids = string.IsNullOrWhiteSpace(request.Ids)
- ? new string[] { }
+ ? Array.Empty<string>()
: request.Ids.Split(',');
- var tasks = ids.Select(i =>
+ foreach (var i in ids)
{
var item = _libraryManager.GetItemById(i);
var auth = _authContext.GetAuthorizationInfo(Request);
- var user = _userManager.GetUserById(auth.UserId);
+ var user = auth.User;
if (!item.CanDelete(user))
{
@@ -740,17 +1028,15 @@ namespace MediaBrowser.Api.Library
throw new SecurityException("Unauthorized access");
}
- return Task.FromResult(true);
+ continue;
}
- return item.Delete(new DeleteOptions
+ _libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = true
- });
- }).ToArray(ids.Length);
-
- Task.WaitAll(tasks);
+ }, true);
+ }
}
/// <summary>
@@ -765,36 +1051,6 @@ namespace MediaBrowser.Api.Library
});
}
- /// <summary>
- /// Gets the critic reviews async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{ItemReviewsResult}.</returns>
- private QueryResult<ItemReview> GetCriticReviews(GetCriticReviews request)
- {
- var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id));
-
- var reviewsArray = reviews.ToArray(reviews.Count);
-
- var result = new QueryResult<ItemReview>
- {
- TotalRecordCount = reviewsArray.Length
- };
-
- if (request.StartIndex.HasValue)
- {
- reviewsArray = reviewsArray.Skip(request.StartIndex.Value).ToArray();
- }
- if (request.Limit.HasValue)
- {
- reviewsArray = reviewsArray.Take(request.Limit.Value).ToArray();
- }
-
- result.Items = reviewsArray;
-
- return result;
- }
-
public object Get(GetThemeMedia request)
{
var themeSongs = GetThemeSongs(new GetThemeSongs
@@ -813,7 +1069,7 @@ namespace MediaBrowser.Api.Library
});
- return ToOptimizedSerializedResultUsingCache(new AllThemeMediaResult
+ return ToOptimizedResult(new AllThemeMediaResult
{
ThemeSongsResult = themeSongs,
ThemeVideosResult = themeVideos,
@@ -831,16 +1087,16 @@ namespace MediaBrowser.Api.Library
{
var result = GetThemeSongs(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
private ThemeMediaResult GetThemeSongs(GetThemeSongs request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id)
- ? (!string.IsNullOrWhiteSpace(request.UserId)
- ? user.RootFolder
+ ? (!request.UserId.Equals(Guid.Empty)
+ ? _libraryManager.GetUserRootFolder()
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
@@ -849,16 +1105,33 @@ namespace MediaBrowser.Api.Library
throw new ResourceNotFoundException("Item not found.");
}
- while (item.ThemeSongIds.Length == 0 && request.InheritFromParent && item.GetParent() != null)
+ BaseItem[] themeItems = Array.Empty<BaseItem>();
+
+ while (true)
{
- item = item.GetParent();
+ themeItems = item.GetThemeSongs().ToArray();
+
+ if (themeItems.Length > 0)
+ {
+ break;
+ }
+
+ if (!request.InheritFromParent)
+ {
+ break;
+ }
+
+ var parent = item.GetParent();
+ if (parent == null)
+ {
+ break;
+ }
+ item = parent;
}
var dtoOptions = GetDtoOptions(_authContext, request);
- var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
- .Where(i => i != null)
- .OrderBy(i => i.SortName)
+ var dtos = themeItems
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
var items = dtos.ToArray();
@@ -867,7 +1140,7 @@ namespace MediaBrowser.Api.Library
{
Items = items,
TotalRecordCount = items.Length,
- OwnerId = _dtoService.GetDtoId(item)
+ OwnerId = item.Id
};
}
@@ -880,16 +1153,16 @@ namespace MediaBrowser.Api.Library
{
var result = GetThemeVideos(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public ThemeMediaResult GetThemeVideos(GetThemeVideos request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id)
- ? (!string.IsNullOrWhiteSpace(request.UserId)
- ? user.RootFolder
+ ? (!request.UserId.Equals(Guid.Empty)
+ ? _libraryManager.GetUserRootFolder()
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
@@ -898,16 +1171,33 @@ namespace MediaBrowser.Api.Library
throw new ResourceNotFoundException("Item not found.");
}
- while (item.ThemeVideoIds.Length == 0 && request.InheritFromParent && item.GetParent() != null)
+ BaseItem[] themeItems = Array.Empty<BaseItem>();
+
+ while (true)
{
- item = item.GetParent();
+ themeItems = item.GetThemeVideos().ToArray();
+
+ if (themeItems.Length > 0)
+ {
+ break;
+ }
+
+ if (!request.InheritFromParent)
+ {
+ break;
+ }
+
+ var parent = item.GetParent();
+ if (parent == null)
+ {
+ break;
+ }
+ item = parent;
}
var dtoOptions = GetDtoOptions(_authContext, request);
- var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
- .Where(i => i != null)
- .OrderBy(i => i.SortName)
+ var dtos = themeItems
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
var items = dtos.ToArray();
@@ -916,43 +1206,8 @@ namespace MediaBrowser.Api.Library
{
Items = items,
TotalRecordCount = items.Length,
- OwnerId = _dtoService.GetDtoId(item)
+ OwnerId = item.Id
};
}
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public object Get(GetYearIndex request)
- {
- var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes)
- ? new string[] { }
- : request.IncludeItemTypes.Split(',');
-
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-
- var query = new InternalItemsQuery(user)
- {
- IncludeItemTypes = includeTypes,
- Recursive = true,
- DtoOptions = new DtoOptions(false)
- {
- EnableImages = false
- }
- };
-
- var items = _libraryManager.GetItemList(query);
-
- var lookup = items
- .ToLookup(i => i.ProductionYear ?? -1)
- .OrderBy(i => i.Key)
- .Select(i => new ItemIndex
- {
- ItemCount = i.Count(),
- Name = i.Key == -1 ? string.Empty : i.Key.ToString(_usCulture)
- })
- .ToList();
-
- return ToOptimizedSerializedResultUsingCache(lookup);
- }
}
}
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index ae488f066..bba89acec 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -210,7 +210,7 @@ namespace MediaBrowser.Api.Library
{
var result = _libraryManager.GetVirtualFolders(true);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public void Post(UpdateLibraryOptions request)
@@ -224,7 +224,7 @@ namespace MediaBrowser.Api.Library
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(AddVirtualFolder request)
+ public Task Post(AddVirtualFolder request)
{
var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
@@ -233,7 +233,7 @@ namespace MediaBrowser.Api.Library
libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
}
- _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
+ return _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
}
/// <summary>
@@ -264,27 +264,27 @@ namespace MediaBrowser.Api.Library
if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
{
- throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
+ throw new ArgumentException("Media library already exists at " + newPath + ".");
}
_libraryMonitor.Stop();
try
{
- // Only make a two-phase move when changing capitalization
+ // Changing capitalization. Handle windows case insensitivity
if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase))
{
- //Create an unique name
- var temporaryName = Guid.NewGuid().ToString();
- var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
- _fileSystem.MoveDirectory(currentPath, temporaryPath);
- currentPath = temporaryPath;
+ var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N"));
+ _fileSystem.MoveDirectory(currentPath, tempPath);
+ currentPath = tempPath;
}
_fileSystem.MoveDirectory(currentPath, newPath);
}
finally
{
+ CollectionFolder.OnCollectionFolderChange();
+
Task.Run(() =>
{
// No need to start if scanning the library because it will handle it
@@ -309,9 +309,9 @@ namespace MediaBrowser.Api.Library
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(RemoveVirtualFolder request)
+ public Task Delete(RemoveVirtualFolder request)
{
- _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary);
+ return _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary);
}
/// <summary>
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index fee52ea5e..510c5f135 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -17,12 +17,15 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Model.IO;
-
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Cryptography;
+using System.Text;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Api.LiveTv
{
@@ -37,13 +40,13 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Channels", "GET", Summary = "Gets available live tv channels.")]
[Authenticated]
- public class GetChannels : IReturn<QueryResult<ChannelInfoDto>>, IHasDtoOptions
+ public class GetChannels : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
{
[ApiMember(Name = "Type", Description = "Optional filter by channel type.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public ChannelType? Type { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -122,7 +125,7 @@ namespace MediaBrowser.Api.LiveTv
if (string.IsNullOrEmpty(val))
{
- return new string[] { };
+ return Array.Empty<string>();
}
return val.Split(',');
@@ -136,7 +139,7 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
[Authenticated]
- public class GetChannel : IReturn<ChannelInfoDto>
+ public class GetChannel : IReturn<BaseItemDto>
{
/// <summary>
/// Gets or sets the id.
@@ -146,7 +149,7 @@ namespace MediaBrowser.Api.LiveTv
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
[Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")]
@@ -157,10 +160,7 @@ namespace MediaBrowser.Api.LiveTv
public string ChannelId { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
-
- [ApiMember(Name = "GroupId", Description = "Optional filter by recording group.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string GroupId { get; set; }
+ public Guid UserId { get; set; }
[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; }
@@ -274,6 +274,14 @@ namespace MediaBrowser.Api.LiveTv
public string UserId { get; set; }
}
+ [Route("/LiveTv/Recordings/Folders", "GET", Summary = "Gets recording folders")]
+ [Authenticated]
+ public class GetRecordingFolders : IReturn<BaseItemDto[]>
+ {
+ [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid UserId { get; set; }
+ }
+
[Route("/LiveTv/Recordings/{Id}", "GET", Summary = "Gets a live tv recording")]
[Authenticated]
public class GetRecording : IReturn<BaseItemDto>
@@ -282,7 +290,7 @@ namespace MediaBrowser.Api.LiveTv
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
[Route("/LiveTv/Tuners/{Id}/Reset", "POST", Summary = "Resets a tv tuner")]
@@ -332,13 +340,14 @@ namespace MediaBrowser.Api.LiveTv
public string ChannelIds { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[ApiMember(Name = "MinStartDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string MinStartDate { get; set; }
[ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasAired { get; set; }
+ public bool? IsAiring { get; set; }
[ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string MaxStartDate { get; set; }
@@ -397,7 +406,7 @@ namespace MediaBrowser.Api.LiveTv
public bool? EnableUserData { get; set; }
public string SeriesTimerId { get; set; }
- public string LibrarySeriesId { get; set; }
+ public Guid LibrarySeriesId { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
@@ -424,7 +433,7 @@ namespace MediaBrowser.Api.LiveTv
}
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[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; }
@@ -481,7 +490,7 @@ namespace MediaBrowser.Api.LiveTv
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
@@ -489,15 +498,15 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
public class DeleteRecording : IReturnVoid
{
- [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+ public Guid Id { get; set; }
}
[Route("/LiveTv/Timers/{Id}", "DELETE", Summary = "Cancels a live tv timer")]
[Authenticated]
public class CancelTimer : IReturnVoid
{
- [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
@@ -536,7 +545,7 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
public class CancelSeriesTimer : IReturnVoid
{
- [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
@@ -566,14 +575,6 @@ namespace MediaBrowser.Api.LiveTv
{
}
- [Route("/LiveTv/Folder", "GET", Summary = "Gets the users live tv folder, along with configured images")]
- [Authenticated]
- public class GetLiveTvFolder : IReturn<BaseItemDto>
- {
- [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
- }
-
[Route("/LiveTv/TunerHosts", "POST", Summary = "Adds a tuner host")]
[Authenticated]
public class AddTunerHost : TunerHostInfo, IReturn<TunerHostInfo>
@@ -600,6 +601,7 @@ namespace MediaBrowser.Api.LiveTv
{
public bool ValidateLogin { get; set; }
public bool ValidateListings { get; set; }
+ public string Pw { get; set; }
}
[Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
@@ -637,7 +639,7 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
public class GetChannelMappingOptions
{
- [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")]
public string ProviderId { get; set; }
}
@@ -645,7 +647,7 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
public class SetChannelMapping
{
- [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")]
public string ProviderId { get; set; }
public string TunerChannelId { get; set; }
public string ProviderChannelId { get; set; }
@@ -659,14 +661,6 @@ namespace MediaBrowser.Api.LiveTv
public string ProviderName { get; set; }
}
- [Route("/LiveTv/Registration", "GET")]
- [Authenticated]
- public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord>
- {
- [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string Feature { get; set; }
- }
-
[Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
public class GetLiveStreamFile
{
@@ -706,8 +700,11 @@ namespace MediaBrowser.Api.LiveTv
private readonly IAuthorizationContext _authContext;
private readonly ISessionContext _sessionContext;
private readonly IEnvironmentInfo _environment;
+ private ICryptoProvider _cryptographyProvider;
+ private IStreamHelper _streamHelper;
+ private IMediaSourceManager _mediaSourceManager;
- public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment)
+ public LiveTvService(ICryptoProvider crypto, IMediaSourceManager mediaSourceManager, IStreamHelper streamHelper, ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@@ -719,6 +716,9 @@ namespace MediaBrowser.Api.LiveTv
_authContext = authContext;
_sessionContext = sessionContext;
_environment = environment;
+ _cryptographyProvider = crypto;
+ _streamHelper = streamHelper;
+ _mediaSourceManager = mediaSourceManager;
}
public object Get(GetTunerHostTypes request)
@@ -727,6 +727,22 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(list);
}
+ public object Get(GetRecordingFolders request)
+ {
+ var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
+ var folders = _liveTvManager.GetRecordingFolders(user);
+
+ var returnArray = _dtoService.GetBaseItemDtos(folders.ToArray(), new DtoOptions(), user);
+
+ var result = new QueryResult<BaseItemDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
+
+ return ToOptimizedResult(result);
+ }
+
public object Get(GetLiveRecordingFile request)
{
var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id);
@@ -740,7 +756,7 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
- return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment)
+ return new ProgressiveFileCopier(_fileSystem, _streamHelper, path, outputHeaders, Logger, _environment)
{
AllowEndOfFile = false
};
@@ -754,12 +770,15 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Get(GetLiveStreamFile request)
{
- var directStreamProvider = (await _liveTvManager.GetEmbyTvLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
+ var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(request.Id, CancellationToken.None).ConfigureAwait(false);
+
+ var directStreamProvider = liveStreamInfo;
+
var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
- return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment)
+ return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger, _environment)
{
AllowEndOfFile = false
};
@@ -770,13 +789,6 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(new ListingsProviderInfo());
}
- public async Task<object> Get(GetLiveTvRegistrationInfo request)
- {
- var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false);
-
- return ToOptimizedResult(result);
- }
-
public async Task<object> Post(SetChannelMapping request)
{
return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelId, request.ProviderChannelId).ConfigureAwait(false);
@@ -828,12 +840,12 @@ namespace MediaBrowser.Api.LiveTv
}).ConfigureAwait(false);
- return ResultFactory.GetResult(response, "application/json");
+ return ResultFactory.GetResult(Request, response, "application/json");
}
private void AssertUserCanManageLiveTv()
{
- var user = _sessionContext.GetUser(Request).Result;
+ var user = _sessionContext.GetUser(Request);
if (user == null)
{
@@ -848,10 +860,26 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Post(AddListingProvider request)
{
+ if (request.Pw != null)
+ {
+ request.Password = GetHashedString(request.Pw);
+ }
+
+ request.Pw = null;
+
var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false);
return ToOptimizedResult(result);
}
+ /// <summary>
+ /// Gets the hashed string.
+ /// </summary>
+ private string GetHashedString(string str)
+ {
+ // legacy
+ return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty).ToLower();
+ }
+
public void Delete(DeleteListingProvider request)
{
_liveTvManager.DeleteListingsProvider(request.Id);
@@ -859,8 +887,6 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Post(AddTunerHost request)
{
- request.EnableNewHdhrChannelIds = true;
-
var result = await _liveTvManager.SaveTunerHost(request).ConfigureAwait(false);
return ToOptimizedResult(result);
}
@@ -888,14 +914,14 @@ namespace MediaBrowser.Api.LiveTv
{
var info = await _liveTvManager.GetLineups(request.Type, request.Id, request.Country, request.Location).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(info);
+ return ToOptimizedResult(info);
}
- public async Task<object> Get(GetLiveTvInfo request)
+ public object Get(GetLiveTvInfo request)
{
- var info = await _liveTvManager.GetLiveTvInfo(CancellationToken.None).ConfigureAwait(false);
+ var info = _liveTvManager.GetLiveTvInfo(CancellationToken.None);
- return ToOptimizedSerializedResultUsingCache(info);
+ return ToOptimizedResult(info);
}
public object Get(GetChannels request)
@@ -923,7 +949,7 @@ namespace MediaBrowser.Api.LiveTv
}, options, CancellationToken.None);
- var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+ var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
RemoveFields(options);
@@ -937,7 +963,7 @@ namespace MediaBrowser.Api.LiveTv
TotalRecordCount = channelResult.TotalRecordCount
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
private void RemoveFields(DtoOptions options)
@@ -955,27 +981,24 @@ namespace MediaBrowser.Api.LiveTv
{
var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
+ var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- public object Get(GetLiveTvFolder request)
- {
- return ToOptimizedResult(_liveTvManager.GetLiveTvFolder(request.UserId, CancellationToken.None));
+ return ToOptimizedResult(result);
}
public async Task<object> Get(GetPrograms request)
{
- var query = new ProgramQuery
+ var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
+
+ var query = new InternalItemsQuery(user)
{
- ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true),
- UserId = request.UserId,
+ ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true).Select(i => new Guid(i)).ToArray(),
HasAired = request.HasAired,
+ IsAiring = request.IsAiring,
EnableTotalRecordCount = request.EnableTotalRecordCount
};
@@ -1009,9 +1032,9 @@ namespace MediaBrowser.Api.LiveTv
query.IsSports = request.IsSports;
query.SeriesTimerId = request.SeriesTimerId;
query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+ query.GenreIds = GetGuids(request.GenreIds);
- if (!string.IsNullOrWhiteSpace(request.LibrarySeriesId))
+ if (!request.LibrarySeriesId.Equals(Guid.Empty))
{
query.IsSeries = true;
@@ -1029,9 +1052,10 @@ namespace MediaBrowser.Api.LiveTv
public object Get(GetRecommendedPrograms request)
{
- var query = new RecommendedProgramQuery
+ var user = _userManager.GetUserById(request.UserId);
+
+ var query = new InternalItemsQuery(user)
{
- UserId = request.UserId,
IsAiring = request.IsAiring,
Limit = request.Limit,
HasAired = request.HasAired,
@@ -1043,7 +1067,7 @@ namespace MediaBrowser.Api.LiveTv
EnableTotalRecordCount = request.EnableTotalRecordCount
};
- query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+ query.GenreIds = GetGuids(request.GenreIds);
var result = _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None);
@@ -1055,16 +1079,14 @@ namespace MediaBrowser.Api.LiveTv
return Get(request);
}
- public async Task<object> Get(GetRecordings request)
+ public object Get(GetRecordings request)
{
var options = GetDtoOptions(_authContext, request);
- options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
- var result = await _liveTvManager.GetRecordings(new RecordingQuery
+ var result = _liveTvManager.GetRecordings(new RecordingQuery
{
ChannelId = request.ChannelId,
UserId = request.UserId,
- GroupId = request.GroupId,
StartIndex = request.StartIndex,
Limit = request.Limit,
Status = request.Status,
@@ -1076,53 +1098,39 @@ namespace MediaBrowser.Api.LiveTv
IsSeries = request.IsSeries,
IsKids = request.IsKids,
IsSports = request.IsSports,
- IsLibraryItem = request.IsLibraryItem
+ IsLibraryItem = request.IsLibraryItem,
+ Fields = request.GetItemFields(),
+ ImageTypeLimit = request.ImageTypeLimit,
+ EnableImages = request.EnableImages
- }, options, CancellationToken.None).ConfigureAwait(false);
+ }, options);
return ToOptimizedResult(result);
}
public object Get(GetRecordingSeries request)
{
- var options = GetDtoOptions(_authContext, request);
- options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
-
- var result = _liveTvManager.GetRecordingSeries(new RecordingQuery
- {
- ChannelId = request.ChannelId,
- UserId = request.UserId,
- GroupId = request.GroupId,
- StartIndex = request.StartIndex,
- Limit = request.Limit,
- Status = request.Status,
- SeriesTimerId = request.SeriesTimerId,
- IsInProgress = request.IsInProgress,
- EnableTotalRecordCount = request.EnableTotalRecordCount
-
- }, options, CancellationToken.None);
-
- return ToOptimizedResult(result);
+ return ToOptimizedResult(new QueryResult<BaseItemDto>());
}
- public async Task<object> Get(GetRecording request)
+ public object Get(GetRecording request)
{
var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
+ var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public async Task<object> Get(GetTimer request)
{
var result = await _liveTvManager.GetTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public async Task<object> Get(GetTimers request)
@@ -1136,34 +1144,31 @@ namespace MediaBrowser.Api.LiveTv
}, CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public void Delete(DeleteRecording request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.DeleteRecording(request.Id);
-
- Task.WaitAll(task);
+ _libraryManager.DeleteItem(_libraryManager.GetItemById(request.Id), new DeleteOptions
+ {
+ DeleteFileLocation = false
+ });
}
- public void Delete(CancelTimer request)
+ public Task Delete(CancelTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.CancelTimer(request.Id);
-
- Task.WaitAll(task);
+ return _liveTvManager.CancelTimer(request.Id);
}
- public void Post(UpdateTimer request)
+ public Task Post(UpdateTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.UpdateTimer(request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _liveTvManager.UpdateTimer(request, CancellationToken.None);
}
public async Task<object> Get(GetSeriesTimers request)
@@ -1175,32 +1180,28 @@ namespace MediaBrowser.Api.LiveTv
}, CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public async Task<object> Get(GetSeriesTimer request)
{
var result = await _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
- public void Delete(CancelSeriesTimer request)
+ public Task Delete(CancelSeriesTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.CancelSeriesTimer(request.Id);
-
- Task.WaitAll(task);
+ return _liveTvManager.CancelSeriesTimer(request.Id);
}
- public void Post(UpdateSeriesTimer request)
+ public Task Post(UpdateSeriesTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None);
}
public async Task<object> Get(GetDefaultTimer request)
@@ -1209,61 +1210,47 @@ namespace MediaBrowser.Api.LiveTv
{
var result = await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
else
{
var result = await _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
}
public async Task<object> Get(GetProgram request)
{
- var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+ var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
var result = await _liveTvManager.GetProgram(request.Id, CancellationToken.None, user).ConfigureAwait(false);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
- public void Post(CreateSeriesTimer request)
+ public Task Post(CreateSeriesTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.CreateSeriesTimer(request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _liveTvManager.CreateSeriesTimer(request, CancellationToken.None);
}
- public void Post(CreateTimer request)
+ public Task Post(CreateTimer request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.CreateTimer(request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _liveTvManager.CreateTimer(request, CancellationToken.None);
}
- public async Task<object> Get(GetRecordingGroups request)
+ public object Get(GetRecordingGroups request)
{
- var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery
- {
- UserId = request.UserId
-
- }, CancellationToken.None).ConfigureAwait(false);
-
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(new QueryResult<BaseItemDto>());
}
- public async Task<object> Get(GetRecordingGroup request)
+ public object Get(GetRecordingGroup request)
{
- var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery(), CancellationToken.None).ConfigureAwait(false);
-
- var group = result.Items.FirstOrDefault(i => string.Equals(i.Id, request.Id, StringComparison.OrdinalIgnoreCase));
-
- return ToOptimizedSerializedResultUsingCache(group);
+ throw new FileNotFoundException();
}
public object Get(GetGuideInfo request)
@@ -1271,13 +1258,11 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(_liveTvManager.GetGuideInfo());
}
- public void Post(ResetTuner request)
+ public Task Post(ResetTuner request)
{
AssertUserCanManageLiveTv();
- var task = _liveTvManager.ResetTuner(request.Id, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _liveTvManager.ResetTuner(request.Id, CancellationToken.None);
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
index 74293ccd9..0a6ccd4c5 100644
--- a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
+++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Api.LiveTv
{
@@ -20,28 +21,30 @@ namespace MediaBrowser.Api.LiveTv
const int StreamCopyToBufferSize = 81920;
- private long _bytesWritten = 0;
public long StartPosition { get; set; }
public bool AllowEndOfFile = true;
private readonly IDirectStreamProvider _directStreamProvider;
private readonly IEnvironmentInfo _environment;
+ private IStreamHelper _streamHelper;
- public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
+ public ProgressiveFileCopier(IFileSystem fileSystem, IStreamHelper streamHelper, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
{
_fileSystem = fileSystem;
_path = path;
_outputHeaders = outputHeaders;
_logger = logger;
_environment = environment;
+ _streamHelper = streamHelper;
}
- public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
+ public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
{
_directStreamProvider = directStreamProvider;
_outputHeaders = outputHeaders;
_logger = logger;
_environment = environment;
+ _streamHelper = streamHelper;
}
public IDictionary<string, string> Headers
@@ -75,7 +78,7 @@ namespace MediaBrowser.Api.LiveTv
var eofCount = 0;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
- var allowAsyncFileRead = _environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
+ var allowAsyncFileRead = true;
using (var inputStream = GetInputStream(allowAsyncFileRead))
{
@@ -89,14 +92,7 @@ namespace MediaBrowser.Api.LiveTv
while (eofCount < emptyReadLimit)
{
int bytesRead;
- if (allowAsyncFileRead)
- {
- bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
- }
+ bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
//var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
@@ -113,49 +109,5 @@ namespace MediaBrowser.Api.LiveTv
}
}
}
-
- private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken)
- {
- var array = new byte[StreamCopyToBufferSize];
- int bytesRead;
- int totalBytesRead = 0;
-
- while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
- {
- var bytesToWrite = bytesRead;
-
- if (bytesToWrite > 0)
- {
- await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
-
- _bytesWritten += bytesRead;
- totalBytesRead += bytesRead;
- }
- }
-
- return totalBytesRead;
- }
-
- private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken)
- {
- var array = new byte[StreamCopyToBufferSize];
- int bytesRead;
- int totalBytesRead = 0;
-
- while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
- {
- var bytesToWrite = bytesRead;
-
- if (bytesToWrite > 0)
- {
- await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
-
- _bytesWritten += bytesRead;
- totalBytesRead += bytesRead;
- }
- }
-
- return totalBytesRead;
- }
}
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 650306ea6..e4c9a0c35 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -1,136 +1,17 @@
-<?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>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Api</RootNamespace>
- <AssemblyName>MediaBrowser.Api</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <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>
- <PlatformTarget>AnyCPU</PlatformTarget>
- </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>
- <PropertyGroup>
- <RunPostBuildEvent>Always</RunPostBuildEvent>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="BrandingService.cs" />
- <Compile Include="ChannelService.cs" />
- <Compile Include="Devices\DeviceService.cs" />
- <Compile Include="Dlna\DlnaServerService.cs" />
- <Compile Include="Dlna\DlnaService.cs" />
- <Compile Include="FilterService.cs" />
- <Compile Include="IHasDtoOptions.cs" />
- <Compile Include="LiveTv\ProgressiveFileCopier.cs" />
- <Compile Include="PlaylistService.cs" />
- <Compile Include="Social\SharingService.cs" />
- <Compile Include="StartupWizardService.cs" />
- <Compile Include="Subtitles\SubtitleService.cs" />
- <Compile Include="Movies\CollectionService.cs" />
- <Compile Include="Music\AlbumsService.cs" />
- <Compile Include="BaseApiService.cs" />
- <Compile Include="ConfigurationService.cs" />
- <Compile Include="DisplayPreferencesService.cs" />
- <Compile Include="EnvironmentService.cs" />
- <Compile Include="GamesService.cs" />
- <Compile Include="IHasItemFields.cs" />
- <Compile Include="Images\ImageByNameService.cs" />
- <Compile Include="Images\ImageRequest.cs" />
- <Compile Include="Images\ImageService.cs" />
- <Compile Include="Music\InstantMixService.cs" />
- <Compile Include="ItemLookupService.cs" />
- <Compile Include="ItemRefreshService.cs" />
- <Compile Include="ItemUpdateService.cs" />
- <Compile Include="Library\LibraryService.cs" />
- <Compile Include="Library\LibraryStructureService.cs" />
- <Compile Include="LiveTv\LiveTvService.cs" />
- <Compile Include="LocalizationService.cs" />
- <Compile Include="Movies\MoviesService.cs" />
- <Compile Include="NewsService.cs" />
- <Compile Include="NotificationsService.cs" />
- <Compile Include="PackageService.cs" />
- <Compile Include="PluginService.cs" />
- <Compile Include="Images\RemoteImageService.cs" />
- <Compile Include="ScheduledTasks\ScheduledTaskService.cs" />
- <Compile Include="ScheduledTasks\ScheduledTasksWebSocketListener.cs" />
- <Compile Include="ApiEntryPoint.cs" />
- <Compile Include="SearchService.cs" />
- <Compile Include="Session\SessionsService.cs" />
- <Compile Include="SimilarItemsHelper.cs" />
- <Compile Include="SuggestionsService.cs" />
- <Compile Include="System\ActivityLogService.cs" />
- <Compile Include="System\ActivityLogWebSocketListener.cs" />
- <Compile Include="System\SystemService.cs" />
- <Compile Include="Movies\TrailersService.cs" />
- <Compile Include="TvShowsService.cs" />
- <Compile Include="UserLibrary\ArtistsService.cs" />
- <Compile Include="UserLibrary\BaseItemsByNameService.cs" />
- <Compile Include="UserLibrary\BaseItemsRequest.cs" />
- <Compile Include="UserLibrary\GameGenresService.cs" />
- <Compile Include="UserLibrary\GenresService.cs" />
- <Compile Include="UserLibrary\ItemsService.cs" />
- <Compile Include="UserLibrary\MusicGenresService.cs" />
- <Compile Include="UserLibrary\PersonsService.cs" />
- <Compile Include="UserLibrary\StudiosService.cs" />
- <Compile Include="UserLibrary\UserLibraryService.cs" />
- <Compile Include="UserLibrary\UserViewsService.cs" />
- <Compile Include="UserLibrary\YearsService.cs" />
- <Compile Include="UserService.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="VideosService.cs" />
- <Compile Include="Session\SessionInfoWebSocketListener.cs" />
- <Compile Include="System\SystemInfoWebSocketListener.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</ItemGroup>
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+
<PropertyGroup>
- <PostBuildEvent>
- </PostBuildEvent>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
- <!-- 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
+
+</Project>
diff --git a/MediaBrowser.Api/MediaBrowser.Api.nuget.targets b/MediaBrowser.Api/MediaBrowser.Api.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.Api/MediaBrowser.Api.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.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs
index c63712f4c..9adefdf75 100644
--- a/MediaBrowser.Api/Movies/CollectionService.cs
+++ b/MediaBrowser.Api/Movies/CollectionService.cs
@@ -3,8 +3,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Collections;
using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Movies
@@ -38,7 +36,7 @@ namespace MediaBrowser.Api.Movies
[Route("/Collections/{Id}/Items", "DELETE", Summary = "Removes items from a collection")]
public class RemoveFromCollection : IReturnVoid
{
- [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string Ids { get; set; }
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
@@ -59,44 +57,40 @@ namespace MediaBrowser.Api.Movies
_authContext = authContext;
}
- public async Task<object> Post(CreateCollection request)
+ public object Post(CreateCollection request)
{
var userId = _authContext.GetAuthorizationInfo(Request).UserId;
var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
- var item = await _collectionManager.CreateCollection(new CollectionCreationOptions
+ var item = _collectionManager.CreateCollection(new CollectionCreationOptions
{
IsLocked = request.IsLocked,
Name = request.Name,
ParentId = parentId,
ItemIdList = SplitValue(request.Ids, ','),
- UserIds = new string[] { userId }
+ UserIds = new [] { userId }
- }).ConfigureAwait(false);
+ });
var dtoOptions = GetDtoOptions(_authContext, request);
var dto = _dtoService.GetBaseItemDto(item, dtoOptions);
- return ToOptimizedResult(new CollectionCreationResult
+ return new CollectionCreationResult
{
Id = dto.Id
- });
+ };
}
public void Post(AddToCollection request)
{
- var task = _collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
-
- Task.WaitAll(task);
+ _collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
}
public void Delete(RemoveFromCollection request)
{
- var task = _collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
-
- Task.WaitAll(task);
+ _collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
}
}
}
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 254c93b33..711280601 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -19,22 +19,6 @@ using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Movies
{
- /// <summary>
- /// Class GetSimilarMovies
- /// </summary>
- [Route("/Movies/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given movie.")]
- public class GetSimilarMovies : BaseGetSimilarItemsFromItem
- {
- }
-
- /// <summary>
- /// Class GetSimilarTrailers
- /// </summary>
- [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
- public class GetSimilarTrailers : BaseGetSimilarItemsFromItem
- {
- }
-
[Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")]
public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasDtoOptions
{
@@ -49,7 +33,7 @@ namespace MediaBrowser.Api.Movies
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
@@ -108,25 +92,6 @@ namespace MediaBrowser.Api.Movies
_authContext = authContext;
}
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetSimilarMovies request)
- {
- var result = GetSimilarItemsResult(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- public object Get(GetSimilarTrailers request)
- {
- var result = GetSimilarItemsResult(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
public object Get(GetMovieRecommendations request)
{
var user = _userManager.GetUserById(request.UserId);
@@ -138,12 +103,12 @@ namespace MediaBrowser.Api.Movies
return ToOptimizedResult(result);
}
- private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request)
+ public QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id) ?
- (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
+ (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
var itemTypes = new List<string> { typeof(Movie).Name };
@@ -182,7 +147,7 @@ namespace MediaBrowser.Api.Movies
{
var categories = new List<RecommendationDto>();
- var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? (Guid?)null : new Guid(parentId);
+ var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId);
var query = new InternalItemsQuery(user)
{
@@ -193,7 +158,7 @@ namespace MediaBrowser.Api.Movies
//typeof(LiveTvProgram).Name
},
// IsMovie = true
- OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
Limit = 7,
ParentId = parentIdGuid,
Recursive = true,
@@ -214,10 +179,10 @@ namespace MediaBrowser.Api.Movies
{
IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
IsMovie = true,
- OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
Limit = 10,
IsFavoriteOrLiked = true,
- ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(recentlyPlayedMovies.Count),
+ ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(recentlyPlayedMovies.Count),
EnableGroupByMetadataKey = true,
ParentId = parentIdGuid,
Recursive = true,
@@ -316,7 +281,7 @@ namespace MediaBrowser.Api.Movies
yield return new RecommendationDto
{
BaselineItemName = name,
- CategoryId = name.GetMD5().ToString("N"),
+ CategoryId = name.GetMD5(),
RecommendationType = type,
Items = returnItems
};
@@ -356,7 +321,7 @@ namespace MediaBrowser.Api.Movies
yield return new RecommendationDto
{
BaselineItemName = name,
- CategoryId = name.GetMD5().ToString("N"),
+ CategoryId = name.GetMD5(),
RecommendationType = type,
Items = returnItems
};
@@ -393,7 +358,7 @@ namespace MediaBrowser.Api.Movies
yield return new RecommendationDto
{
BaselineItemName = item.Name,
- CategoryId = item.Id.ToString("N"),
+ CategoryId = item.Id,
RecommendationType = type,
Items = returnItems
};
@@ -405,7 +370,7 @@ namespace MediaBrowser.Api.Movies
{
var people = _libraryManager.GetPeople(new InternalPeopleQuery
{
- ExcludePersonTypes = new List<string>
+ ExcludePersonTypes = new []
{
PersonType.Director
},
diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs
index d7986fa50..538840f6c 100644
--- a/MediaBrowser.Api/Music/AlbumsService.cs
+++ b/MediaBrowser.Api/Music/AlbumsService.cs
@@ -64,7 +64,7 @@ namespace MediaBrowser.Api.Music
request, new[] { typeof(MusicArtist) },
SimilarItemsHelper.GetSimiliarityScore);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -85,7 +85,7 @@ namespace MediaBrowser.Api.Music
request, new[] { typeof(MusicAlbum) },
GetAlbumSimilarityScore);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index 8a18298f1..8435b1ac1 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -29,13 +29,6 @@ namespace MediaBrowser.Api.Music
{
}
- [Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")]
- public class GetInstantMixFromArtist : BaseGetSimilarItems
- {
- [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Name { get; set; }
- }
-
[Route("/MusicGenres/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")]
public class GetInstantMixFromMusicGenre : BaseGetSimilarItems
{
@@ -170,18 +163,6 @@ namespace MediaBrowser.Api.Music
return GetResult(items, user, request, dtoOptions);
}
- public object Get(GetInstantMixFromArtist request)
- {
- var user = _userManager.GetUserById(request.UserId);
- var artist = _libraryManager.GetArtist(request.Name, new DtoOptions(false));
-
- var dtoOptions = GetDtoOptions(_authContext, request);
-
- var items = _musicManager.GetInstantMixFromArtist(artist, user, dtoOptions);
-
- return GetResult(items, user, request, dtoOptions);
- }
-
private object GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
{
var list = items;
@@ -200,7 +181,7 @@ namespace MediaBrowser.Api.Music
result.Items = returnList;
- return ToOptimizedResult(result);
+ return result;
}
}
diff --git a/MediaBrowser.Api/NewsService.cs b/MediaBrowser.Api/NewsService.cs
index 542103799..cbc77bfc9 100644
--- a/MediaBrowser.Api/NewsService.cs
+++ b/MediaBrowser.Api/NewsService.cs
@@ -40,7 +40,7 @@ namespace MediaBrowser.Api
});
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
}
}
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index 79dda8702..366d1318b 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -49,7 +49,7 @@ namespace MediaBrowser.Api
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string PackageType { get; set; }
- [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET", AllowMultiple = true)]
+ [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string TargetSystems { get; set; }
[ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
@@ -146,24 +146,24 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetPackageVersionUpdates request)
+ public async Task<object> Get(GetPackageVersionUpdates request)
{
PackageVersionInfo[] result = null;
if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
{
- result = _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToArray();
+ result = (await _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).ConfigureAwait(false)).ToArray();
}
else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
{
- var updateCheckResult = _appHost
- .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).Result;
+ var updateCheckResult = await _appHost
+ .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).ConfigureAwait(false);
if (updateCheckResult.IsUpdateAvailable)
{
- result = new PackageVersionInfo[] {updateCheckResult.Package};
+ result = new PackageVersionInfo[] { updateCheckResult.Package };
}
}
@@ -224,11 +224,11 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <exception cref="ResourceNotFoundException"></exception>
- public void Post(InstallPackage request)
+ public async Task Post(InstallPackage request)
{
var package = string.IsNullOrEmpty(request.Version) ?
- _installationManager.GetLatestCompatibleVersion(request.Name, request.AssemblyGuid, _appHost.ApplicationVersion, request.UpdateClass).Result :
- _installationManager.GetPackage(request.Name, request.AssemblyGuid, request.UpdateClass, Version.Parse(request.Version)).Result;
+ await _installationManager.GetLatestCompatibleVersion(request.Name, request.AssemblyGuid, _appHost.ApplicationVersion, request.UpdateClass).ConfigureAwait(false) :
+ await _installationManager.GetPackage(request.Name, request.AssemblyGuid, request.UpdateClass, Version.Parse(request.Version)).ConfigureAwait(false);
if (package == null)
{
@@ -244,7 +244,7 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Delete(CancelPackageInstallation request)
{
- var info = _installationManager.CurrentInstallations.FirstOrDefault(i => string.Equals(i.Item1.Id, request.Id));
+ var info = _installationManager.CurrentInstallations.FirstOrDefault(i => i.Item1.Id.Equals(request.Id));
if (info != null)
{
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 226678021..471e3bb57 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -9,6 +9,7 @@ using MediaBrowser.Model.Querying;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Extensions;
+using System;
namespace MediaBrowser.Api
{
@@ -21,10 +22,10 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Ids", Description = "Item Ids to add to the playlist", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string Ids { get; set; }
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public Guid UserId { get; set; }
- [ApiMember(Name = "MediaType", Description = "The playlist media type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "MediaType", Description = "The playlist media type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MediaType { get; set; }
}
@@ -42,7 +43,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
[Route("/Playlists/{Id}/Items/{ItemId}/Move/{NewIndex}", "POST", Summary = "Moves a playlist item")]
@@ -76,14 +77,14 @@ namespace MediaBrowser.Api
public class GetPlaylistItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
{
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -139,9 +140,7 @@ namespace MediaBrowser.Api
public void Post(MoveItem request)
{
- var task = _playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex);
-
- Task.WaitAll(task);
+ _playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex);
}
public async Task<object> Post(CreatePlaylist request)
@@ -149,7 +148,7 @@ namespace MediaBrowser.Api
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
{
Name = request.Name,
- ItemIdList = SplitValue(request.Ids, ','),
+ ItemIdList = GetGuids(request.Ids),
UserId = request.UserId,
MediaType = request.MediaType
@@ -160,22 +159,18 @@ namespace MediaBrowser.Api
public void Post(AddToPlaylist request)
{
- var task = _playlistManager.AddToPlaylist(request.Id, request.Ids.Split(','), request.UserId);
-
- Task.WaitAll(task);
+ _playlistManager.AddToPlaylist(request.Id, GetGuids(request.Ids), request.UserId);
}
public void Delete(RemoveFromPlaylist request)
{
- var task = _playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(','));
-
- Task.WaitAll(task);
+ _playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(','));
}
public object Get(GetPlaylistItems request)
{
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var items = playlist.GetManageableItems().ToArray();
diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs
index 1eea89431..1ee096a2e 100644
--- a/MediaBrowser.Api/PluginService.cs
+++ b/MediaBrowser.Api/PluginService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Registration;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
@@ -15,6 +14,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Common.Plugins;
namespace MediaBrowser.Api
{
@@ -103,9 +103,6 @@ namespace MediaBrowser.Api
{
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
-
- [ApiMember(Name = "Mb2Equivalent", Description = "Optional. The equivalent feature name in MB2", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string Mb2Equivalent { get; set; }
}
[Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
@@ -116,6 +113,14 @@ namespace MediaBrowser.Api
public string Name { get; set; }
}
+ public class RegistrationInfo
+ {
+ public string Name { get; set; }
+ public DateTime ExpirationDate { get; set; }
+ public bool IsTrial { get; set; }
+ public bool IsRegistered { get; set; }
+ }
+
[Route("/Appstore/Register", "POST", Summary = "Registers an appstore sale", IsHidden = true)]
[Authenticated]
public class RegisterAppstoreSale
@@ -168,7 +173,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public async Task<object> Get(GetRegistrationStatus request)
{
- var result = await _securityManager.GetRegistrationStatus(request.Name, request.Mb2Equivalent).ConfigureAwait(false);
+ var result = await _securityManager.GetRegistrationStatus(request.Name).ConfigureAwait(false);
return ToOptimizedResult(result);
}
@@ -235,7 +240,7 @@ namespace MediaBrowser.Api
}
}
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -246,7 +251,7 @@ namespace MediaBrowser.Api
public object Get(GetPluginConfiguration request)
{
var guid = new Guid(request.Id);
- var plugin = _appHost.Plugins.First(p => p.Id == guid);
+ var plugin = _appHost.Plugins.First(p => p.Id == guid) as IHasPluginConfiguration;
return ToOptimizedResult(plugin.Configuration);
}
@@ -256,15 +261,15 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetPluginSecurityInfo request)
+ public async Task<object> Get(GetPluginSecurityInfo request)
{
var result = new PluginSecurityInfo
{
- IsMBSupporter = _securityManager.IsMBSupporter,
+ IsMBSupporter = await _securityManager.IsSupporter().ConfigureAwait(false),
SupporterKey = _securityManager.SupporterKey
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -272,37 +277,38 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
- public void Post(RegisterAppstoreSale request)
+ public Task Post(RegisterAppstoreSale request)
{
- var task = _securityManager.RegisterAppStoreSale(request.Parameters);
-
- Task.WaitAll(task);
+ return _securityManager.RegisterAppStoreSale(request.Parameters);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdatePluginSecurityInfo request)
+ public Task Post(UpdatePluginSecurityInfo request)
{
- var info = request;
-
- _securityManager.SupporterKey = info.SupporterKey;
+ return _securityManager.UpdateSupporterKey(request.SupporterKey);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdatePluginConfiguration request)
+ public async Task Post(UpdatePluginConfiguration request)
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var id = new Guid(GetPathValue(1));
- var plugin = _appHost.Plugins.First(p => p.Id == id);
+ var plugin = _appHost.Plugins.First(p => p.Id == id) as IHasPluginConfiguration;
+
+ if (plugin == null)
+ {
+ throw new FileNotFoundException();
+ }
- var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration;
+ var configuration = (await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, plugin.ConfigurationType).ConfigureAwait(false)) as BasePluginConfiguration;
plugin.UpdateConfiguration(configuration);
}
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
index abe3e5407..959bdcd55 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
@@ -207,7 +207,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
}
- TaskManager.Execute(task);
+ TaskManager.Execute(task, new TaskOptions());
}
/// <summary>
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
index 2d30625a9..ff57b4dd1 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
@@ -33,8 +33,8 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTasksWebSocketListener" /> class.
/// </summary>
- public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager, ITimerFactory timerFactory)
- : base(logger, timerFactory)
+ public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager)
+ : base(logger)
{
TaskManager = taskManager;
@@ -72,14 +72,6 @@ namespace MediaBrowser.Api.ScheduledTasks
.Where(i => !i.IsHidden));
}
- protected override bool SendOnTimer
- {
- get
- {
- return false;
- }
- }
-
protected override void Dispose(bool dispose)
{
TaskManager.TaskExecuting -= TaskManager_TaskExecuting;
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 50033eee8..2e363136b 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Model.Search;
using System.Threading.Tasks;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Services;
+using System;
namespace MediaBrowser.Api
{
@@ -39,7 +40,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Supply a user id to search within a user's library or omit to search all.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Search characters used to find items
@@ -134,11 +135,11 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public async Task<object> Get(GetSearchHints request)
+ public object Get(GetSearchHints request)
{
- var result = await GetSearchHintsAsync(request).ConfigureAwait(false);
+ var result = GetSearchHintsAsync(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -146,9 +147,9 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task{IEnumerable{SearchHintResult}}.</returns>
- private async Task<SearchHintResult> GetSearchHintsAsync(GetSearchHints request)
+ private SearchHintResult GetSearchHintsAsync(GetSearchHints request)
{
- var result = await _searchEngine.GetSearchHints(new SearchQuery
+ var result = _searchEngine.GetSearchHints(new SearchQuery
{
Limit = request.Limit,
SearchTerm = request.SearchTerm,
@@ -170,7 +171,7 @@ namespace MediaBrowser.Api
IsSeries = request.IsSeries,
IsSports = request.IsSports
- }).ConfigureAwait(false);
+ });
return new SearchHintResult
{
@@ -194,7 +195,7 @@ namespace MediaBrowser.Api
Name = item.Name,
IndexNumber = item.IndexNumber,
ParentIndexNumber = item.ParentIndexNumber,
- ItemId = _dtoService.GetDtoId(item),
+ Id = item.Id,
Type = item.GetClientTypeName(),
MediaType = item.MediaType,
MatchedTerm = hintInfo.MatchedTerm,
@@ -204,6 +205,14 @@ namespace MediaBrowser.Api
EndDate = item.EndDate
};
+ // legacy
+ result.ItemId = result.Id;
+
+ if (item.IsFolder)
+ {
+ result.IsFolder = true;
+ }
+
var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
if (primaryImageTag != null)
@@ -221,7 +230,7 @@ namespace MediaBrowser.Api
result.StartDate = program.StartDate;
}
- var hasSeries = item as IHasSeries;
+ var hasSeries = item as IHasSeriesName;
if (hasSeries != null)
{
result.Series = hasSeries.SeriesName;
@@ -256,7 +265,7 @@ namespace MediaBrowser.Api
if (album != null)
{
result.Album = album.Name;
- result.AlbumId = album.Id.ToString("N");
+ result.AlbumId = album.Id;
}
else
{
@@ -264,7 +273,7 @@ namespace MediaBrowser.Api
}
}
- if (!string.IsNullOrWhiteSpace(item.ChannelId))
+ if (!item.ChannelId.Equals(Guid.Empty))
{
var channel = _libraryManager.GetItemById(item.ChannelId);
result.ChannelName = channel == null ? null : channel.Name;
diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
index 65c69fc7d..ca2c381cf 100644
--- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
- class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfoDto>, WebSocketListenerState>
+ class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
@@ -33,8 +33,8 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Initializes a new instance of the <see cref="SessionInfoWebSocketListener"/> class.
/// </summary>
- public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager, ITimerFactory timerFactory)
- : base(logger, timerFactory)
+ public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager)
+ : base(logger)
{
_sessionManager = sessionManager;
@@ -87,17 +87,9 @@ namespace MediaBrowser.Api.Session
/// </summary>
/// <param name="state">The state.</param>
/// <returns>Task{SystemInfo}.</returns>
- protected override Task<IEnumerable<SessionInfoDto>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
+ protected override Task<IEnumerable<SessionInfo>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
{
- return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto));
- }
-
- protected override bool SendOnTimer
- {
- get
- {
- return false;
- }
+ return Task.FromResult(_sessionManager.Sessions);
}
protected override void Dispose(bool dispose)
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 35c29cd15..1056b3c8b 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -11,6 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
using MediaBrowser.Controller;
+using MediaBrowser.Model.Dto;
namespace MediaBrowser.Api.Session
{
@@ -19,13 +20,15 @@ namespace MediaBrowser.Api.Session
/// </summary>
[Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
[Authenticated]
- public class GetSessions : IReturn<SessionInfoDto[]>
+ public class GetSessions : IReturn<SessionInfo[]>
{
[ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string ControllableByUserId { get; set; }
+ public Guid ControllableByUserId { get; set; }
[ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
+
+ public int? ActiveWithinSeconds { get; set; }
}
/// <summary>
@@ -189,7 +192,7 @@ namespace MediaBrowser.Api.Session
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Game, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
@@ -198,9 +201,6 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "SupportedCommands", Description = "A list of supported remote control commands, comma delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string SupportedCommands { get; set; }
- [ApiMember(Name = "MessageCallbackUrl", Description = "A url to post messages to, including remote control commands.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string MessageCallbackUrl { get; set; }
-
[ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsMediaControl { get; set; }
@@ -210,8 +210,6 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsPersistentIdentifier { get; set; }
- public bool SupportsContentUploading { get; set; }
-
public PostCapabilities()
{
SupportsPersistentIdentifier = true;
@@ -226,7 +224,7 @@ namespace MediaBrowser.Api.Session
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
}
@@ -242,11 +240,17 @@ namespace MediaBrowser.Api.Session
{
}
+ [Route("/Auth/Providers", "GET")]
+ [Authenticated(Roles = "Admin")]
+ public class GetAuthProviders : IReturn<NameIdPair[]>
+ {
+ }
+
[Route("/Auth/Keys/{Key}", "DELETE")]
[Authenticated(Roles = "Admin")]
public class RevokeKey
{
- [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Key { get; set; }
}
@@ -286,6 +290,11 @@ namespace MediaBrowser.Api.Session
_appHost = appHost;
}
+ public object Get(GetAuthProviders request)
+ {
+ return _userManager.GetAuthenticationProviders();
+ }
+
public void Delete(RevokeKey request)
{
_sessionManager.RevokeToken(request.Key);
@@ -297,14 +306,13 @@ namespace MediaBrowser.Api.Session
_authRepo.Create(new AuthenticationInfo
{
AppName = request.App,
- IsActive = true,
AccessToken = Guid.NewGuid().ToString("N"),
DateCreated = DateTime.UtcNow,
DeviceId = _appHost.SystemId,
DeviceName = _appHost.FriendlyName,
AppVersion = _appHost.ApplicationVersion.ToString()
- }, CancellationToken.None);
+ });
}
public void Post(ReportSessionEnded request)
@@ -318,11 +326,10 @@ namespace MediaBrowser.Api.Session
{
var result = _authRepo.Get(new AuthenticationInfoQuery
{
- IsActive = true,
HasUser = false
});
- return ToOptimizedResult(result);
+ return result;
}
/// <summary>
@@ -332,27 +339,33 @@ namespace MediaBrowser.Api.Session
/// <returns>System.Object.</returns>
public object Get(GetSessions request)
{
- var result = _sessionManager.Sessions.Where(i => i.IsActive);
+ var result = _sessionManager.Sessions;
if (!string.IsNullOrEmpty(request.DeviceId))
{
result = result.Where(i => string.Equals(i.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase));
}
- if (!string.IsNullOrWhiteSpace(request.ControllableByUserId))
+ if (!request.ControllableByUserId.Equals(Guid.Empty))
{
- result = result.Where(i => i.SupportsMediaControl);
+ result = result.Where(i => i.SupportsRemoteControl);
var user = _userManager.GetUserById(request.ControllableByUserId);
if (!user.Policy.EnableRemoteControlOfOtherUsers)
{
- result = result.Where(i => !i.UserId.HasValue || i.ContainsUser(request.ControllableByUserId));
+ result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId));
}
if (!user.Policy.EnableSharedDeviceControl)
{
- result = result.Where(i => i.UserId.HasValue);
+ result = result.Where(i => !i.UserId.Equals(Guid.Empty));
+ }
+
+ if (request.ActiveWithinSeconds.HasValue && request.ActiveWithinSeconds.Value > 0)
+ {
+ var minActiveDate = DateTime.UtcNow.AddSeconds(0 - request.ActiveWithinSeconds.Value);
+ result = result.Where(i => i.LastActivityDate >= minActiveDate);
}
result = result.Where(i =>
@@ -361,7 +374,7 @@ namespace MediaBrowser.Api.Session
if (!string.IsNullOrWhiteSpace(deviceId))
{
- if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), deviceId))
+ if (!_deviceManager.CanAccessDevice(user, deviceId))
{
return false;
}
@@ -371,21 +384,19 @@ namespace MediaBrowser.Api.Session
});
}
- return ToOptimizedResult(result.Select(_sessionManager.GetSessionInfoDto).ToArray());
+ return ToOptimizedResult(result.ToArray());
}
- public void Post(SendPlaystateCommand request)
+ public Task Post(SendPlaystateCommand request)
{
- var task = _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Result.Id, request.Id, request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(DisplayContent request)
+ public Task Post(DisplayContent request)
{
var command = new BrowseRequest
{
@@ -394,16 +405,14 @@ namespace MediaBrowser.Api.Session
ItemType = request.ItemType
};
- var task = _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(SendSystemCommand request)
+ public Task Post(SendSystemCommand request)
{
GeneralCommandType commandType;
var name = request.Command;
@@ -413,24 +422,22 @@ namespace MediaBrowser.Api.Session
name = commandType.ToString();
}
- var currentSession = GetSession(_sessionContext).Result;
+ var currentSession = GetSession(_sessionContext);
var command = new GeneralCommand
{
Name = name,
- ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null
+ ControllingUserId = currentSession.UserId
};
- var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(SendMessageCommand request)
+ public Task Post(SendMessageCommand request)
{
var command = new MessageCommand
{
@@ -439,63 +446,55 @@ namespace MediaBrowser.Api.Session
Text = request.Text
};
- var task = _sessionManager.SendMessageCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendMessageCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(Play request)
+ public Task Post(Play request)
{
- var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendPlayCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None);
}
- public void Post(SendGeneralCommand request)
+ public Task Post(SendGeneralCommand request)
{
- var currentSession = GetSession(_sessionContext).Result;
+ var currentSession = GetSession(_sessionContext);
var command = new GeneralCommand
{
Name = request.Command,
- ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null
+ ControllingUserId = currentSession.UserId
};
- var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
}
- public void Post(SendFullGeneralCommand request)
+ public Task Post(SendFullGeneralCommand request)
{
- var currentSession = GetSession(_sessionContext).Result;
+ var currentSession = GetSession(_sessionContext);
- request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null;
+ request.ControllingUserId = currentSession.UserId;
- var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None);
-
- Task.WaitAll(task);
+ return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None);
}
public void Post(AddUserToSession request)
{
- _sessionManager.AddAdditionalUser(request.Id, request.UserId);
+ _sessionManager.AddAdditionalUser(request.Id, new Guid(request.UserId));
}
public void Delete(RemoveUserFromSession request)
{
- _sessionManager.RemoveAdditionalUser(request.Id, request.UserId);
+ _sessionManager.RemoveAdditionalUser(request.Id, new Guid(request.UserId));
}
public void Post(PostCapabilities request)
{
if (string.IsNullOrWhiteSpace(request.Id))
{
- request.Id = GetSession(_sessionContext).Result.Id;
+ request.Id = GetSession(_sessionContext).Id;
}
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
@@ -505,12 +504,8 @@ namespace MediaBrowser.Api.Session
SupportsMediaControl = request.SupportsMediaControl,
- MessageCallbackUrl = request.MessageCallbackUrl,
-
SupportsSync = request.SupportsSync,
- SupportsContentUploading = request.SupportsContentUploading,
-
SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
});
}
@@ -519,7 +514,7 @@ namespace MediaBrowser.Api.Session
{
if (string.IsNullOrWhiteSpace(request.Id))
{
- request.Id = GetSession(_sessionContext).Result.Id;
+ request.Id = GetSession(_sessionContext).Id;
}
_sessionManager.ReportCapabilities(request.Id, request);
}
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 0b5eaa476..be9c1a4c5 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -49,7 +49,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// The maximum number of items to return
@@ -73,10 +73,10 @@ namespace MediaBrowser.Api
{
internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id) ?
- (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
+ (!request.UserId.Equals(Guid.Empty) ? libraryManager.GetUserRootFolder() :
libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
var query = new InternalItemsQuery(user)
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api
// ExcludeArtistIds
if (!string.IsNullOrEmpty(request.ExcludeArtistIds))
{
- query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|');
+ query.ExcludeArtistIds = BaseApiService.GetGuids(request.ExcludeArtistIds);
}
var inputItems = libraryManager.GetItemList(query);
diff --git a/MediaBrowser.Api/Social/SharingService.cs b/MediaBrowser.Api/Social/SharingService.cs
deleted file mode 100644
index 4f10667b7..000000000
--- a/MediaBrowser.Api/Social/SharingService.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Social;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api.Social
-{
- [Route("/Social/Shares/{Id}", "GET", Summary = "Gets a share")]
- [Authenticated]
- public class GetSocialShareInfo : IReturn<SocialShareInfo>
- {
- [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Social/Shares/Public/{Id}", "GET", Summary = "Gets a share")]
- public class GetPublicSocialShareInfo : IReturn<SocialShareInfo>
- {
- [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Social/Shares/Public/{Id}/Image", "GET", Summary = "Gets a share")]
- public class GetShareImage
- {
- [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Social/Shares", "POST", Summary = "Creates a share")]
- [Authenticated]
- public class CreateShare : IReturn<SocialShareInfo>
- {
- [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string ItemId { get; set; }
-
- [ApiMember(Name = "UserId", Description = "The user id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string UserId { get; set; }
- }
-
- [Route("/Social/Shares/{Id}", "DELETE", Summary = "Deletes a share")]
- [Authenticated]
- public class DeleteShare : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Social/Shares/Public/{Id}/Item", "GET", Summary = "Gets a share")]
- public class GetSharedLibraryItem
- {
- [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- public class SharingService : BaseApiService
- {
- private readonly ISharingManager _sharingManager;
- private readonly ILibraryManager _libraryManager;
- private readonly IDlnaManager _dlnaManager;
- private readonly IDtoService _dtoService;
- private readonly IHttpResultFactory _resultFactory;
-
- public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService, IHttpResultFactory resultFactory)
- {
- _sharingManager = sharingManager;
- _dlnaManager = dlnaManager;
- _libraryManager = libraryManager;
- _dtoService = dtoService;
- _resultFactory = resultFactory;
- }
-
- public object Get(GetSocialShareInfo request)
- {
- var info = _sharingManager.GetShareInfo(request.Id);
-
- return ToOptimizedResult(info);
- }
-
- public object Get(GetSharedLibraryItem request)
- {
- var info = _sharingManager.GetShareInfo(request.Id);
-
- if (info.ExpirationDate <= DateTime.UtcNow)
- {
- throw new ResourceNotFoundException();
- }
-
- var item = _libraryManager.GetItemById(info.ItemId);
-
- var dto = _dtoService.GetBaseItemDto(item, new DtoOptions());
-
- return ToOptimizedResult(dto);
- }
-
- public object Get(GetPublicSocialShareInfo request)
- {
- var info = _sharingManager.GetShareInfo(request.Id);
-
- if (info.ExpirationDate <= DateTime.UtcNow)
- {
- throw new ResourceNotFoundException();
- }
-
- return ToOptimizedResult(info);
- }
-
- public async Task<object> Post(CreateShare request)
- {
- var info = await _sharingManager.CreateShare(request.ItemId, request.UserId).ConfigureAwait(false);
-
- return ToOptimizedResult(info);
- }
-
- public void Delete(DeleteShare request)
- {
- _sharingManager.DeleteShare(request.Id);
- }
-
- public async Task<object> Get(GetShareImage request)
- {
- var share = _sharingManager.GetShareInfo(request.Id);
-
- if (share == null)
- {
- throw new ResourceNotFoundException();
- }
- if (share.ExpirationDate <= DateTime.UtcNow)
- {
- throw new ResourceNotFoundException();
- }
-
- var item = _libraryManager.GetItemById(share.ItemId);
-
- var image = item.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null)
- {
- if (image.IsLocalFile)
- {
- return await _resultFactory.GetStaticFileResult(Request, image.Path).ConfigureAwait(false);
- }
-
- try
- {
- // Don't fail the request over this
- var updatedImage = await _libraryManager.ConvertImageToLocal(item, image, 0).ConfigureAwait(false);
- return await _resultFactory.GetStaticFileResult(Request, updatedImage.Path).ConfigureAwait(false);
- }
- catch
- {
-
- }
- }
-
- // Grab a dlna icon if nothing else is available
- using (var response = _dlnaManager.GetIcon("logo240.jpg"))
- {
- using (var ms = new MemoryStream())
- {
- response.Stream.CopyTo(ms);
-
- ms.Position = 0;
- var bytes = ms.ToArray();
- return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower());
- }
- }
-
- }
- }
-}
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index c6345c17f..61d9a90d6 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
using System;
using System.Linq;
using System.Threading.Tasks;
@@ -14,32 +13,34 @@ using System.Threading;
namespace MediaBrowser.Api
{
- [Route("/Startup/Complete", "POST", Summary = "Reports that the startup wizard has been completed")]
+ [Route("/Startup/Complete", "POST", Summary = "Reports that the startup wizard has been completed", IsHidden = true)]
public class ReportStartupWizardComplete : IReturnVoid
{
}
- [Route("/Startup/Info", "GET", Summary = "Gets initial server info")]
- public class GetStartupInfo : IReturn<StartupInfo>
+ [Route("/Startup/Configuration", "GET", Summary = "Gets initial server configuration", IsHidden = true)]
+ public class GetStartupConfiguration : IReturn<StartupConfiguration>
{
}
- [Route("/Startup/Configuration", "GET", Summary = "Gets initial server configuration")]
- public class GetStartupConfiguration : IReturn<StartupConfiguration>
+ [Route("/Startup/Configuration", "POST", Summary = "Updates initial server configuration", IsHidden = true)]
+ public class UpdateStartupConfiguration : StartupConfiguration, IReturnVoid
{
}
- [Route("/Startup/Configuration", "POST", Summary = "Updates initial server configuration")]
- public class UpdateStartupConfiguration : StartupConfiguration, IReturnVoid
+ [Route("/Startup/RemoteAccess", "POST", Summary = "Updates initial server configuration", IsHidden = true)]
+ public class UpdateRemoteAccessConfiguration : IReturnVoid
{
+ public bool EnableRemoteAccess { get; set; }
+ public bool EnableAutomaticPortMapping { get; set; }
}
- [Route("/Startup/User", "GET", Summary = "Gets initial user info")]
+ [Route("/Startup/User", "GET", Summary = "Gets initial user info", IsHidden = true)]
public class GetStartupUser : IReturn<StartupUser>
{
}
- [Route("/Startup/User", "POST", Summary = "Updates initial user info")]
+ [Route("/Startup/User", "POST", Summary = "Updates initial user info", IsHidden = true)]
public class UpdateStartupUser : StartupUser, IReturn<UpdateStartupUserResult>
{
}
@@ -67,7 +68,6 @@ namespace MediaBrowser.Api
public void Post(ReportStartupWizardComplete request)
{
_config.Configuration.IsStartupWizardCompleted = true;
- _config.Configuration.AutoRunWebApp = true;
_config.SetOptimalValues();
_config.SaveConfiguration();
@@ -101,14 +101,6 @@ namespace MediaBrowser.Api
}
}
- public object Get(GetStartupInfo request)
- {
- return new StartupInfo
- {
- HasMediaEncoder = !string.IsNullOrWhiteSpace(_mediaEncoder.EncoderPath)
- };
- }
-
public object Get(GetStartupConfiguration request)
{
var result = new StartupConfiguration
@@ -129,6 +121,13 @@ namespace MediaBrowser.Api
_config.SaveConfiguration();
}
+ public void Post(UpdateRemoteAccessConfiguration request)
+ {
+ _config.Configuration.EnableRemoteAccess = request.EnableRemoteAccess;
+ _config.Configuration.EnableUPnP = request.EnableAutomaticPortMapping;
+ _config.SaveConfiguration();
+ }
+
public object Get(GetStartupUser request)
{
var user = _userManager.Users.First();
@@ -152,11 +151,11 @@ namespace MediaBrowser.Api
if (!string.IsNullOrWhiteSpace(user.ConnectUserName) &&
string.IsNullOrWhiteSpace(request.ConnectUserName))
{
- await _connectManager.RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false);
+ await _connectManager.RemoveConnect(user).ConfigureAwait(false);
}
else if (!string.Equals(user.ConnectUserName, request.ConnectUserName, StringComparison.OrdinalIgnoreCase))
{
- result.UserLinkResult = await _connectManager.LinkUser(user.Id.ToString("N"), request.ConnectUserName).ConfigureAwait(false);
+ result.UserLinkResult = await _connectManager.LinkUser(user, request.ConnectUserName).ConfigureAwait(false);
}
return result;
@@ -170,11 +169,6 @@ namespace MediaBrowser.Api
public string PreferredMetadataLanguage { get; set; }
}
- public class StartupInfo
- {
- public bool HasMediaEncoder { get; set; }
- }
-
public class StartupUser
{
public string Name { get; set; }
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index 4d4b4cb27..c8b0a32e9 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Api.Subtitles
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "DELETE")]
public int Index { get; set; }
@@ -40,7 +40,7 @@ namespace MediaBrowser.Api.Subtitles
public class SearchRemoteSubtitles : IReturn<RemoteSubtitleInfo[]>
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
[ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Language { get; set; }
@@ -48,20 +48,12 @@ namespace MediaBrowser.Api.Subtitles
public bool? IsPerfectMatch { get; set; }
}
- [Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")]
- [Authenticated]
- public class GetSubtitleProviders : IReturn<SubtitleProviderInfo[]>
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
[Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")]
[Authenticated]
public class DownloadRemoteSubtitles : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
[ApiMember(Name = "SubtitleId", Description = "SubtitleId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string SubtitleId { get; set; }
@@ -84,7 +76,7 @@ namespace MediaBrowser.Api.Subtitles
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
@@ -123,7 +115,7 @@ namespace MediaBrowser.Api.Subtitles
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }
- [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
public int SegmentLength { get; set; }
}
@@ -195,7 +187,7 @@ namespace MediaBrowser.Api.Subtitles
builder.AppendLine("#EXT-X-ENDLIST");
- return ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
+ return ResultFactory.GetResult(Request, builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
}
public async Task<object> Get(GetSubtitle request)
@@ -206,10 +198,11 @@ namespace MediaBrowser.Api.Subtitles
}
if (string.IsNullOrEmpty(request.Format))
{
- var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
+ var item = (Video)_libraryManager.GetItemById(request.Id);
+ var idString = request.Id.ToString("N");
var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null)
- .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id));
+ .First(i => string.Equals(i.Id, request.MediaSourceId ?? idString));
var subtitleStream = mediaSource.MediaStreams
.First(i => i.Type == MediaStreamType.Subtitle && i.Index == request.Index);
@@ -227,22 +220,24 @@ namespace MediaBrowser.Api.Subtitles
text = text.Replace("WEBVTT", "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000");
- return ResultFactory.GetResult(text, MimeTypes.GetMimeType("file." + request.Format));
+ return ResultFactory.GetResult(Request, text, MimeTypes.GetMimeType("file." + request.Format));
}
}
}
- return ResultFactory.GetResult(await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format));
+ return ResultFactory.GetResult(Request, await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format));
}
private Task<Stream> GetSubtitles(GetSubtitle request)
{
- return _subtitleEncoder.GetSubtitles(request.Id,
+ var item = _libraryManager.GetItemById(request.Id);
+
+ return _subtitleEncoder.GetSubtitles(item,
request.MediaSourceId,
request.Index,
request.Format,
request.StartPositionTicks,
- request.EndPositionTicks,
+ request.EndPositionTicks ?? 0,
request.CopyTimestamps,
CancellationToken.None);
}
@@ -251,30 +246,20 @@ namespace MediaBrowser.Api.Subtitles
{
var video = (Video)_libraryManager.GetItemById(request.Id);
- var response = await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false);
-
- return ToOptimizedResult(response);
- }
-
- public void Delete(DeleteSubtitle request)
- {
- var task = _subtitleManager.DeleteSubtitles(request.Id, request.Index);
-
- Task.WaitAll(task);
+ return await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false);
}
- public object Get(GetSubtitleProviders request)
+ public Task Delete(DeleteSubtitle request)
{
- var result = _subtitleManager.GetProviders(request.Id);
-
- return ToOptimizedResult(result);
+ var item = _libraryManager.GetItemById(request.Id);
+ return _subtitleManager.DeleteSubtitles(item, request.Index);
}
public async Task<object> Get(GetRemoteSubtitles request)
{
var result = await _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).ConfigureAwait(false);
- return ResultFactory.GetResult(result.Stream, MimeTypes.GetMimeType("file." + result.Format));
+ return ResultFactory.GetResult(Request, result.Stream, MimeTypes.GetMimeType("file." + result.Format));
}
public void Post(DownloadRemoteSubtitles request)
diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs
index 3b918d8a2..07053c554 100644
--- a/MediaBrowser.Api/SuggestionsService.cs
+++ b/MediaBrowser.Api/SuggestionsService.cs
@@ -14,11 +14,11 @@ using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Api
{
[Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")]
- public class GetSuggestedItems : IReturn<QueryResult<BaseItem>>
+ public class GetSuggestedItems : IReturn<QueryResult<BaseItemDto>>
{
public string MediaType { get; set; }
public string Type { get; set; }
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
public bool EnableTotalRecordCount { get; set; }
public int? StartIndex { get; set; }
public int? Limit { get; set; }
@@ -51,14 +51,12 @@ namespace MediaBrowser.Api
public object Get(GetSuggestedItems request)
{
- var result = GetResultItems(request);
-
- return ToOptimizedResult(result);
+ return GetResultItems(request);
}
private QueryResult<BaseItemDto> GetResultItems(GetSuggestedItems request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var dtoOptions = GetDtoOptions(_authContext, request);
var result = GetItems(request, user, dtoOptions);
@@ -81,7 +79,7 @@ namespace MediaBrowser.Api
{
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
MediaTypes = request.GetMediaTypes(),
IncludeItemTypes = request.GetIncludeItemTypes(),
IsVirtualItem = false,
diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs
index e3a18a933..d55c57ffa 100644
--- a/MediaBrowser.Api/System/ActivityLogService.cs
+++ b/MediaBrowser.Api/System/ActivityLogService.cs
@@ -24,8 +24,10 @@ namespace MediaBrowser.Api.System
[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; }
- [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string MinDate { get; set; }
+
+ public bool? HasUserId { get; set; }
}
[Authenticated(Roles = "Admin")]
@@ -44,7 +46,7 @@ namespace MediaBrowser.Api.System
(DateTime?)null :
DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
- var result = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit);
+ var result = _activityManager.GetActivityLogEntries(minDate, request.HasUserId, request.StartIndex, request.Limit);
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index f9cac7389..6991244c6 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Threading;
using System.Threading;
namespace MediaBrowser.Api.System
@@ -28,7 +27,7 @@ namespace MediaBrowser.Api.System
/// </summary>
private readonly IActivityManager _activityManager;
- public ActivityLogWebSocketListener(ILogger logger, ITimerFactory timerFactory, IActivityManager activityManager) : base(logger, timerFactory)
+ public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger)
{
_activityManager = activityManager;
_activityManager.EntryCreated += _activityManager_EntryCreated;
@@ -48,14 +47,7 @@ namespace MediaBrowser.Api.System
{
return Task.FromResult(new List<ActivityLogEntry>());
}
-
- protected override bool SendOnTimer
- {
- get
- {
- return false;
- }
- }
+
protected override void Dispose(bool dispose)
{
diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
deleted file mode 100644
index 63847f2b5..000000000
--- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.System;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Threading;
-using System.Threading;
-
-namespace MediaBrowser.Api.System
-{
- /// <summary>
- /// Class SystemInfoWebSocketListener
- /// </summary>
- public class SystemInfoWebSocketListener : BasePeriodicWebSocketListener<SystemInfo, WebSocketListenerState>
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- protected override string Name
- {
- get { return "SystemInfo"; }
- }
-
- /// <summary>
- /// The _kernel
- /// </summary>
- private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SystemInfoWebSocketListener" /> class.
- /// </summary>
- public SystemInfoWebSocketListener(ILogger logger, IServerApplicationHost appHost, ITimerFactory timerFactory)
- : base(logger, timerFactory)
- {
- _appHost = appHost;
- }
-
- /// <summary>
- /// Gets the data to send.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>Task{SystemInfo}.</returns>
- protected override Task<SystemInfo> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
- {
- return _appHost.GetSystemInfo(cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index c0bbf70ea..d2880f735 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -35,6 +35,7 @@ namespace MediaBrowser.Api.System
}
[Route("/System/Ping", "POST")]
+ [Route("/System/Ping", "GET")]
public class PingSystem : IReturnVoid
{
@@ -79,6 +80,13 @@ namespace MediaBrowser.Api.System
public string Name { get; set; }
}
+ [Route("/System/WakeOnLanInfo", "GET", Summary = "Gets wake on lan information")]
+ [Authenticated]
+ public class GetWakeOnLanInfo : IReturn<WakeOnLanInfo[]>
+ {
+
+ }
+
/// <summary>
/// Class SystemInfoService
/// </summary>
@@ -116,6 +124,13 @@ namespace MediaBrowser.Api.System
return _appHost.Name;
}
+ public object Get(GetWakeOnLanInfo request)
+ {
+ var result = _appHost.GetWakeOnLanInfo();
+
+ return ToOptimizedResult(result);
+ }
+
public object Get(GetServerLogs request)
{
IEnumerable<FileSystemMetadata> files;
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index fd81a9a3e..6cbb4062d 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -88,7 +88,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -131,11 +131,6 @@ namespace MediaBrowser.Api
public bool? EnableUserData { get; set; }
}
- [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
- public class GetSimilarShows : BaseGetSimilarItemsFromItem
- {
- }
-
[Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
public class GetEpisodes : IReturn<QueryResult<BaseItemDto>>, IHasItemFields, IHasDtoOptions
{
@@ -144,7 +139,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
@@ -212,7 +207,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
@@ -288,66 +283,20 @@ namespace MediaBrowser.Api
_authContext = authContext;
}
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetSimilarShows request)
- {
- var result = GetSimilarItemsResult(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request)
- {
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-
- var item = string.IsNullOrEmpty(request.Id) ?
- (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
- _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
-
- var dtoOptions = GetDtoOptions(_authContext, request);
-
- var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- Limit = request.Limit,
- IncludeItemTypes = new[]
- {
- typeof(Series).Name
- },
- SimilarTo = item,
- DtoOptions = dtoOptions
-
- });
-
- var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnList,
-
- TotalRecordCount = itemsResult.Count
- };
-
- return result;
- }
-
public object Get(GetUpcomingEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
var minPremiereDate = DateTime.Now.Date.ToUniversalTime().AddDays(-1);
- var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+ var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId);
var options = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Episode).Name },
- OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
MinPremiereDate = minPremiereDate,
StartIndex = request.StartIndex,
Limit = request.Limit,
@@ -365,7 +314,7 @@ namespace MediaBrowser.Api
Items = returnItems
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -391,7 +340,7 @@ namespace MediaBrowser.Api
var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user);
- return ToOptimizedSerializedResultUsingCache(new QueryResult<BaseItemDto>
+ return ToOptimizedResult(new QueryResult<BaseItemDto>
{
TotalRecordCount = result.TotalRecordCount,
Items = returnItems
diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
index 4018759d9..cd3c80463 100644
--- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
@@ -40,7 +40,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -56,9 +56,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>System.Object.</returns>
public object Get(GetArtist request)
{
- var result = GetItem(request);
-
- return ToOptimizedResult(result);
+ return GetItem(request);
}
/// <summary>
@@ -71,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
var item = GetArtist(request.Name, LibraryManager, dtoOptions);
-
- if (!string.IsNullOrWhiteSpace(request.UserId))
+
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -94,9 +92,7 @@ namespace MediaBrowser.Api.UserLibrary
//request.IncludeItemTypes = "Audio,MusicVideo";
}
- var result = GetResultSlim(request);
-
- return ToOptimizedResult(result);
+ return GetResultSlim(request);
}
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index ed0c4069b..fe8b446a1 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -56,10 +56,10 @@ namespace MediaBrowser.Api.UserLibrary
{
BaseItem parentItem;
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
- parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
+ parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId);
}
else
{
@@ -73,18 +73,12 @@ namespace MediaBrowser.Api.UserLibrary
{
var parent = GetParentItem(request);
- var collectionFolder = parent as ICollectionFolder;
+ var collectionFolder = parent as IHasCollectionType;
if (collectionFolder != null)
{
return collectionFolder.CollectionType;
}
- var view = parent as UserView;
- if (view != null)
- {
- return view.ViewType;
- }
-
return null;
}
@@ -95,10 +89,10 @@ namespace MediaBrowser.Api.UserLibrary
User user = null;
BaseItem parentItem;
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
user = UserManager.GetUserById(request.UserId);
- parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
+ parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId);
}
else
{
@@ -123,25 +117,27 @@ namespace MediaBrowser.Api.UserLibrary
Tags = request.GetTags(),
OfficialRatings = request.GetOfficialRatings(),
Genres = request.GetGenres(),
- GenreIds = request.GetGenreIds(),
- StudioIds = request.GetStudioIds(),
+ GenreIds = GetGuids(request.GenreIds),
+ StudioIds = GetGuids(request.StudioIds),
Person = request.Person,
- PersonIds = request.GetPersonIds(),
+ PersonIds = GetGuids(request.PersonIds),
PersonTypes = request.GetPersonTypes(),
Years = request.GetYears(),
MinCommunityRating = request.MinCommunityRating,
- DtoOptions = dtoOptions
+ DtoOptions = dtoOptions,
+ SearchTerm = request.SearchTerm,
+ EnableTotalRecordCount = request.EnableTotalRecordCount
};
if (!string.IsNullOrWhiteSpace(request.ParentId))
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { request.ParentId };
+ query.AncestorIds = new[] { new Guid(request.ParentId) };
}
else
{
- query.ItemIds = new[] { request.ParentId };
+ query.ItemIds = new[] { new Guid(request.ParentId) };
}
}
@@ -158,7 +154,7 @@ namespace MediaBrowser.Api.UserLibrary
{
return null;
}
- }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray();
+ }).Where(i => i != null).Select(i => i.Id).ToArray();
}
foreach (var filter in request.GetFilters())
@@ -197,10 +193,9 @@ namespace MediaBrowser.Api.UserLibrary
var result = GetItems(request, query);
- var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions);
var dtos = result.Items.Select(i =>
{
- var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, syncProgess, user);
+ var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, user);
if (!string.IsNullOrWhiteSpace(request.IncludeItemTypes))
{
@@ -247,10 +242,10 @@ namespace MediaBrowser.Api.UserLibrary
User user = null;
BaseItem parentItem;
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
user = UserManager.GetUserById(request.UserId);
- parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
+ parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId);
}
else
{
@@ -277,7 +272,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var folder = (Folder)parentItem;
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
items = request.Recursive ?
folder.GetRecursiveChildren(user, query).ToList() :
@@ -297,9 +292,7 @@ namespace MediaBrowser.Api.UserLibrary
var extractedItems = GetAllItems(request, items);
- var filteredItems = FilterItems(request, extractedItems, user);
-
- filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy());
+ var filteredItems = LibraryManager.Sort(extractedItems, user, request.GetOrderBy());
var ibnItemsArray = filteredItems.ToList();
@@ -326,8 +319,7 @@ namespace MediaBrowser.Api.UserLibrary
var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
- var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions);
- var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, syncProgess, user));
+ var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user));
result.Items = dtos.Where(i => i != null).ToArray();
@@ -338,125 +330,6 @@ namespace MediaBrowser.Api.UserLibrary
/// Filters the items.
/// </summary>
/// <param name="request">The request.</param>
- /// <param name="items">The items.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{`0}.</returns>
- private IEnumerable<BaseItem> FilterItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
- {
- if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
- {
- items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
- }
- if (!string.IsNullOrEmpty(request.NameStartsWith))
- {
- items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
- }
-
- if (!string.IsNullOrEmpty(request.NameLessThan))
- {
- items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
- }
-
- var imageTypes = request.GetImageTypes();
- if (imageTypes.Length > 0)
- {
- items = items.Where(item => imageTypes.Any(item.HasImage));
- }
-
- var filters = request.GetFilters();
-
- if (filters.Contains(ItemFilter.Dislikes))
- {
- items = items.Where(i =>
- {
- var userdata = UserDataRepository.GetUserData(user, i);
-
- return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
- });
- }
-
- if (filters.Contains(ItemFilter.Likes))
- {
- items = items.Where(i =>
- {
- var userdata = UserDataRepository.GetUserData(user, i);
-
- return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
- });
- }
-
- if (filters.Contains(ItemFilter.IsFavoriteOrLikes))
- {
- items = items.Where(i =>
- {
- var userdata = UserDataRepository.GetUserData(user, i);
-
- var likes = userdata.Likes ?? false;
- var favorite = userdata.IsFavorite;
-
- return likes || favorite;
- });
- }
-
- if (filters.Contains(ItemFilter.IsFavorite))
- {
- items = items.Where(i =>
- {
- var userdata = UserDataRepository.GetUserData(user, i);
-
- return userdata != null && userdata.IsFavorite;
- });
- }
-
- // Avoid implicitly captured closure
- var currentRequest = request;
- return items.Where(i => ApplyAdditionalFilters(currentRequest, i, user, false));
- }
-
- private bool ApplyAdditionalFilters(BaseItemsRequest request, BaseItem i, User user, bool isPreFiltered)
- {
- if (!isPreFiltered)
- {
- // Apply tag filter
- var tags = request.GetTags();
- if (tags.Length > 0)
- {
- if (!tags.Any(v => i.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- // Apply official rating filter
- var officialRatings = request.GetOfficialRatings();
- if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty))
- {
- return false;
- }
-
- // Apply genre filter
- var genres = request.GetGenres();
- if (genres.Length > 0 && !genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
-
- // Apply year filter
- var years = request.GetYears();
- if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value)))
- {
- return false;
- }
- }
-
- return true;
- }
-
-
- /// <summary>
- /// Filters the items.
- /// </summary>
- /// <param name="request">The request.</param>
/// <param name="f">The f.</param>
/// <param name="excludeItemTypes">The exclude item types.</param>
/// <param name="includeItemTypes">The include item types.</param>
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 88d080db5..11c12c718 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -58,6 +58,8 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHD { get; set; }
+ public bool? Is4K { get; set; }
+
[ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string LocationTypes { get; set; }
@@ -103,9 +105,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasTvdbId { get; set; }
- [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsInBoxSet { get; set; }
-
+ [ApiMember(Name = "ExcludeItemIds", Description = "Optional. If specified, results will be filtered by exxcluding item ids. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ExcludeItemIds { get; set; }
public bool EnableTotalRecordCount { get; set; }
@@ -131,6 +131,8 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Recursive", Description = "When searching within folders, this determines whether or not the search will be recursive. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool Recursive { get; set; }
+ public string SearchTerm { get; set; }
+
/// <summary>
/// Gets or sets the sort order.
/// </summary>
@@ -277,6 +279,10 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "ArtistIds", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ArtistIds { get; set; }
+ public string AlbumArtistIds { get; set; }
+
+ public string ContributingArtistIds { get; set; }
+
[ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Albums { get; set; }
@@ -300,8 +306,8 @@ namespace MediaBrowser.Api.UserLibrary
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the min offical rating.
@@ -321,6 +327,12 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? CollapseBoxSetItems { get; set; }
+
+ public int? MinWidth { get; set; }
+ public int? MinHeight { get; set; }
+ public int? MaxWidth { get; set; }
+ public int? MaxHeight { get; set; }
+
/// <summary>
/// Gets or sets the video formats.
/// </summary>
@@ -369,11 +381,6 @@ namespace MediaBrowser.Api.UserLibrary
return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
- public string[] GetExcludeItemIds()
- {
- return (ExcludeItemIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
public string[] GetExcludeItemTypes()
{
return (ExcludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
@@ -389,36 +396,11 @@ namespace MediaBrowser.Api.UserLibrary
return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
- public string[] GetArtistIds()
- {
- return (ArtistIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetStudioIds()
- {
- return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetGenreIds()
- {
- return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
public string[] GetPersonTypes()
{
return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
- public string[] GetPersonIds()
- {
- return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetItemIds()
- {
- return (Ids ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
public VideoType[] GetVideoTypes()
{
var val = VideoTypes;
@@ -467,18 +449,18 @@ namespace MediaBrowser.Api.UserLibrary
/// Gets the order by.
/// </summary>
/// <returns>IEnumerable{ItemSortBy}.</returns>
- public Tuple<string, SortOrder>[] GetOrderBy()
+ public ValueTuple<string, SortOrder>[] GetOrderBy()
{
return GetOrderBy(SortBy, SortOrder);
}
- public static Tuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder)
+ public static ValueTuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder)
{
var val = sortBy;
if (string.IsNullOrEmpty(val))
{
- return new Tuple<string, Model.Entities.SortOrder>[] { };
+ return Array.Empty<ValueTuple<string, Model.Entities.SortOrder>>();
}
var vals = val.Split(',');
@@ -489,7 +471,7 @@ namespace MediaBrowser.Api.UserLibrary
var sortOrders = requestedSortOrder.Split(',');
- var result = new Tuple<string, Model.Entities.SortOrder>[vals.Length];
+ var result = new ValueTuple<string, Model.Entities.SortOrder>[vals.Length];
for (var i = 0; i < vals.Length; i++)
{
@@ -498,7 +480,7 @@ namespace MediaBrowser.Api.UserLibrary
var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null;
var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase) ? MediaBrowser.Model.Entities.SortOrder.Descending : MediaBrowser.Model.Entities.SortOrder.Ascending;
- result[i] = new Tuple<string, Model.Entities.SortOrder>(vals[i], sortOrder);
+ result[i] = new ValueTuple<string, Model.Entities.SortOrder>(vals[i], sortOrder);
}
return result;
diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
index 0b2ca4daf..476e881b9 100644
--- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
[Authenticated]
@@ -46,7 +46,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -60,7 +60,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = GetGameGenre(request.Name, LibraryManager, dtoOptions);
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -79,7 +79,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetResultSlim(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index d913f52d9..0bb7d7865 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -56,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -70,7 +70,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = GetGenre(request.Name, LibraryManager, dtoOptions);
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetResultSlim(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 1e531ba66..aa17e85f3 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -88,25 +88,25 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+ var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId);
var options = GetDtoOptions(_authContext, request);
- var ancestorIds = new List<string>();
+ var ancestorIds = new List<Guid>();
var excludeFolderIds = user.Configuration.LatestItemsExcludes;
- if (!parentIdGuid.HasValue && excludeFolderIds.Length > 0)
+ if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
{
- ancestorIds = user.RootFolder.GetChildren(user, true)
+ ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !excludeFolderIds.Contains(i.Id.ToString("N")))
- .Select(i => i.Id.ToString("N"))
+ .Select(i => i.Id)
.ToList();
}
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
IsResumable = true,
StartIndex = request.StartIndex,
Limit = request.Limit,
@@ -119,7 +119,8 @@ namespace MediaBrowser.Api.UserLibrary
EnableTotalRecordCount = request.EnableTotalRecordCount,
AncestorIds = ancestorIds.ToArray(),
IncludeItemTypes = request.GetIncludeItemTypes(),
- ExcludeItemTypes = request.GetExcludeItemTypes()
+ ExcludeItemTypes = request.GetExcludeItemTypes(),
+ SearchTerm = request.SearchTerm
});
var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user);
@@ -130,7 +131,7 @@ namespace MediaBrowser.Api.UserLibrary
Items = returnItems
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -147,7 +148,7 @@ namespace MediaBrowser.Api.UserLibrary
var result = GetItems(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -156,7 +157,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="request">The request.</param>
private QueryResult<BaseItemDto> GetItems(GetItems request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var dtoOptions = GetDtoOptions(_authContext, request);
@@ -191,26 +192,23 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
{
- var item = string.IsNullOrEmpty(request.ParentId) ?
- null :
- _libraryManager.GetItemById(request.ParentId);
-
if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
{
- if (item == null || user != null)
- {
- item = _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, "PlaylistsFolder", StringComparison.OrdinalIgnoreCase));
- }
+ request.ParentId = null;
}
else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
{
- item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+ request.ParentId = null;
}
+ var item = string.IsNullOrEmpty(request.ParentId) ?
+ null :
+ _libraryManager.GetItemById(request.ParentId);
+
if (item == null)
{
item = string.IsNullOrEmpty(request.ParentId) ?
- user == null ? _libraryManager.RootFolder : user.RootFolder :
+ user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder() :
_libraryManager.GetItemById(request.ParentId);
}
@@ -222,6 +220,15 @@ namespace MediaBrowser.Api.UserLibrary
folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder();
}
+ var hasCollectionType = folder as IHasCollectionType;
+ var isPlaylistQuery = (hasCollectionType != null && string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase));
+
+ if (isPlaylistQuery)
+ {
+ request.Recursive = true;
+ request.IncludeItemTypes = "Playlist";
+ }
+
if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
{
return folder.GetItems(GetItemsQuery(request, dtoOptions, user));
@@ -266,8 +273,10 @@ namespace MediaBrowser.Api.UserLibrary
HasImdbId = request.HasImdbId,
IsPlaceHolder = request.IsPlaceHolder,
IsLocked = request.IsLocked,
- IsInBoxSet = request.IsInBoxSet,
- IsHD = request.IsHD,
+ MinWidth = request.MinWidth,
+ MinHeight = request.MinHeight,
+ MaxWidth = request.MaxWidth,
+ MaxHeight = request.MaxHeight,
Is3D = request.Is3D,
HasTvdbId = request.HasTvdbId,
HasTmdbId = request.HasTmdbId,
@@ -279,33 +288,37 @@ namespace MediaBrowser.Api.UserLibrary
HasThemeSong = request.HasThemeSong,
HasThemeVideo = request.HasThemeVideo,
HasTrailer = request.HasTrailer,
+ IsHD = request.IsHD,
+ Is4K = request.Is4K,
Tags = request.GetTags(),
OfficialRatings = request.GetOfficialRatings(),
Genres = request.GetGenres(),
- ArtistIds = request.GetArtistIds(),
- GenreIds = request.GetGenreIds(),
- StudioIds = request.GetStudioIds(),
+ ArtistIds = GetGuids(request.ArtistIds),
+ AlbumArtistIds = GetGuids(request.AlbumArtistIds),
+ ContributingArtistIds = GetGuids(request.ContributingArtistIds),
+ GenreIds = GetGuids(request.GenreIds),
+ StudioIds = GetGuids(request.StudioIds),
Person = request.Person,
- PersonIds = request.GetPersonIds(),
+ PersonIds = GetGuids(request.PersonIds),
PersonTypes = request.GetPersonTypes(),
Years = request.GetYears(),
ImageTypes = request.GetImageTypes(),
VideoTypes = request.GetVideoTypes(),
AdjacentTo = request.AdjacentTo,
- ItemIds = request.GetItemIds(),
+ ItemIds = GetGuids(request.Ids),
MinPlayers = request.MinPlayers,
MaxPlayers = request.MaxPlayers,
MinCommunityRating = request.MinCommunityRating,
MinCriticRating = request.MinCriticRating,
- ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId),
+ ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId),
ParentIndexNumber = request.ParentIndexNumber,
- AiredDuringSeason = request.AiredDuringSeason,
EnableTotalRecordCount = request.EnableTotalRecordCount,
- ExcludeItemIds = request.GetExcludeItemIds(),
- DtoOptions = dtoOptions
+ ExcludeItemIds = GetGuids(request.ExcludeItemIds),
+ DtoOptions = dtoOptions,
+ SearchTerm = request.SearchTerm
};
- if (!string.IsNullOrWhiteSpace(request.Ids))
+ if (!string.IsNullOrWhiteSpace(request.Ids) || !string.IsNullOrWhiteSpace(request.SearchTerm))
{
query.CollapseBoxSetItems = false;
}
@@ -416,18 +429,18 @@ namespace MediaBrowser.Api.UserLibrary
{
return null;
}
- }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray();
+ }).Where(i => i != null).Select(i => i.Id).ToArray();
}
// ExcludeArtistIds
if (!string.IsNullOrWhiteSpace(request.ExcludeArtistIds))
{
- query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|');
+ query.ExcludeArtistIds = GetGuids(request.ExcludeArtistIds);
}
if (!string.IsNullOrWhiteSpace(request.AlbumIds))
{
- query.AlbumIds = request.AlbumIds.Split('|');
+ query.AlbumIds = GetGuids(request.AlbumIds);
}
// Albums
@@ -441,7 +454,7 @@ namespace MediaBrowser.Api.UserLibrary
Name = i,
Limit = 1
- }).Select(albumId => albumId.ToString("N"));
+ }).Select(albumId => albumId);
}).ToArray();
}
@@ -459,7 +472,7 @@ namespace MediaBrowser.Api.UserLibrary
{
return null;
}
- }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray();
+ }).Where(i => i != null).Select(i => i.Id).ToArray();
}
// Apply default sorting if none requested
@@ -468,10 +481,10 @@ namespace MediaBrowser.Api.UserLibrary
// Albums by artist
if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], "MusicAlbum", StringComparison.OrdinalIgnoreCase))
{
- query.OrderBy = new Tuple<string, SortOrder>[]
+ query.OrderBy = new []
{
- new Tuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending),
- new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending)
+ new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending),
+ new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending)
};
}
}
diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
index 36dc773d4..d4f1b3fa8 100644
--- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
[Authenticated]
@@ -47,7 +47,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -61,7 +61,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = GetMusicGenre(request.Name, LibraryManager, dtoOptions);
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -80,7 +80,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetResultSlim(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index 9417447d8..d2c9ef33a 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -7,6 +7,8 @@ using MediaBrowser.Model.Dto;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Services;
+using System;
+using MediaBrowser.Model.Querying;
namespace MediaBrowser.Api.UserLibrary
{
@@ -36,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -54,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -67,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
var item = GetPerson(request.Name, LibraryManager, dtoOptions);
-
- if (!string.IsNullOrWhiteSpace(request.UserId))
+
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -85,9 +87,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>System.Object.</returns>
public object Get(GetPersons request)
{
- var result = GetResult(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
+ return GetResultSlim(request);
}
/// <summary>
@@ -98,48 +98,22 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
{
- var inputPersonTypes = ((GetPersons)request).PersonTypes;
- var personTypes = string.IsNullOrEmpty(inputPersonTypes) ? new string[] { } : inputPersonTypes.Split(',');
-
- // Either get all people, or all people filtered by a specific person type
- var allPeople = GetAllPeople(items, personTypes);
-
- return allPeople
- .Select(i => i.Name)
- .DistinctNames()
-
- .Select(name =>
- {
- try
- {
- return LibraryManager.GetPerson(name);
- }
- catch
- {
- return null;
- // Already logged at lower levels
- }
- }
-
- ).Where(i => i != null);
+ throw new NotImplementedException();
}
- /// <summary>
- /// Gets all people.
- /// </summary>
- /// <param name="itemsList">The items list.</param>
- /// <param name="personTypes">The person types.</param>
- /// <returns>IEnumerable{PersonInfo}.</returns>
- private IEnumerable<PersonInfo> GetAllPeople(IList<BaseItem> itemsList, string[] personTypes)
+ protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
{
- var allIds = itemsList.Select(i => i.Id).ToArray();
-
- var allPeople = LibraryManager.GetPeople(new InternalPeopleQuery
+ var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery
{
- PersonTypes = personTypes
+ PersonTypes = query.PersonTypes,
+ NameContains = query.NameContains ?? query.SearchTerm
});
- return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type);
+ return new QueryResult<Tuple<BaseItem, ItemCounts>>
+ {
+ TotalRecordCount = items.Count,
+ Items = items.Take(query.Limit ?? int.MaxValue).Select(i => new Tuple<BaseItem, ItemCounts>(i, new ItemCounts())).ToArray()
+ };
}
public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext)
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index f10cccbb1..4a3204f71 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -56,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -70,7 +70,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = GetStudio(request.Name, LibraryManager, dtoOptions);
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetResultSlim(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
@@ -105,10 +105,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
{
- return items
- .SelectMany(i => i.Studios)
- .DistinctNames()
- .Select(name => LibraryManager.GetStudio(name));
+ throw new NotImplementedException();
}
public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext)
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 1bbc740c0..30df0ad23 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -50,7 +50,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -64,7 +64,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the item id.
@@ -85,14 +85,14 @@ namespace MediaBrowser.Api.UserLibrary
/// </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; }
+ public Guid 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; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -106,14 +106,14 @@ namespace MediaBrowser.Api.UserLibrary
/// </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; }
+ public Guid 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; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -127,14 +127,14 @@ namespace MediaBrowser.Api.UserLibrary
/// </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; }
+ public Guid 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; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -148,14 +148,14 @@ namespace MediaBrowser.Api.UserLibrary
/// </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; }
+ public Guid 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; }
+ public Guid Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
@@ -176,7 +176,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -197,7 +197,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -215,13 +215,13 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
[ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int Limit { get; set; }
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string ParentId { get; set; }
+ public Guid ParentId { get; set; }
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
@@ -291,7 +291,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetAsync(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public object Get(GetLatestMedia request)
@@ -344,43 +344,15 @@ namespace MediaBrowser.Api.UserLibrary
var user = _userManager.GetUserById(request.UserId);
var item = string.IsNullOrEmpty(request.Id) ?
- user.RootFolder :
+ _libraryManager.GetUserRootFolder() :
_libraryManager.GetItemById(request.Id);
- var series = item as Series;
-
- // Get them from the child tree
- if (series != null)
- {
- var dtoOptions = GetDtoOptions(_authContext, request);
-
- // Avoid implicitly captured closure
- var currentUser = user;
-
- var dtos = series
- .GetEpisodes(user, dtoOptions)
- .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
- .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, currentUser));
-
- return dtos.ToArray();
- }
-
- var movie = item as IHasSpecialFeatures;
-
- // Get them from the db
- if (movie != null)
- {
- var dtoOptions = GetDtoOptions(_authContext, request);
-
- var dtos = movie.SpecialFeatureIds
- .Select(_libraryManager.GetItemById)
- .OrderBy(i => i.SortName)
- .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
+ var dtoOptions = GetDtoOptions(_authContext, request);
- return dtos.ToArray();
- }
+ var dtos = item.GetDisplayExtras()
+ .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
- return new BaseItemDto[] { };
+ return dtos.ToArray();
}
/// <summary>
@@ -392,28 +364,15 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
-
- List<Guid> trailerIds = null;
-
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- trailerIds = hasTrailers.GetTrailerIds();
- }
- else
- {
- trailerIds = new List<Guid>();
- }
+ var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
var dtoOptions = GetDtoOptions(_authContext, request);
- var dtos = trailerIds
- .Select(_libraryManager.GetItemById)
+ var dtos = item.GetExtras(new[] { ExtraType.Trailer })
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
.ToArray();
- return ToOptimizedSerializedResultUsingCache(dtos);
+ return ToOptimizedResult(dtos);
}
/// <summary>
@@ -425,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
+ var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
@@ -433,7 +392,7 @@ namespace MediaBrowser.Api.UserLibrary
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
private async Task RefreshItemOnDemandIfNeeded(BaseItem item)
@@ -448,7 +407,7 @@ namespace MediaBrowser.Api.UserLibrary
var options = new MetadataRefreshOptions(_fileSystem)
{
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ImageRefreshMode = ImageRefreshMode.FullRefresh,
+ ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ForceSave = performFullRefresh
};
@@ -466,13 +425,13 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var item = user.RootFolder;
+ var item = _libraryManager.GetUserRootFolder();
var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -484,7 +443,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
+ var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
@@ -498,7 +457,7 @@ namespace MediaBrowser.Api.UserLibrary
TotalRecordCount = dtos.Length
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -507,7 +466,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="request">The request.</param>
public object Post(MarkFavoriteItem request)
{
- var dto = MarkFavorite(request.UserId, request.Id, true);
+ var dto = MarkFavorite(request.UserId, request.Id, true);
return ToOptimizedResult(dto);
}
@@ -529,11 +488,11 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="userId">The user id.</param>
/// <param name="itemId">The item id.</param>
/// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
- private UserItemDataDto MarkFavorite(string userId, string itemId, bool isFavorite)
+ private UserItemDataDto MarkFavorite(Guid userId, Guid itemId, bool isFavorite)
{
var user = _userManager.GetUserById(userId);
- var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
+ var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
@@ -541,7 +500,7 @@ namespace MediaBrowser.Api.UserLibrary
// Set favorite status
data.IsFavorite = isFavorite;
- _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
+ _userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
return _userDataRepository.GetUserDataDto(item, user);
}
@@ -563,7 +522,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="request">The request.</param>
public object Post(UpdateUserItemRating request)
{
- var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes);
+ var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes);
return ToOptimizedResult(dto);
}
@@ -574,18 +533,18 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="userId">The user id.</param>
/// <param name="itemId">The item id.</param>
/// <param name="likes">if set to <c>true</c> [likes].</param>
- private UserItemDataDto UpdateUserItemRating(string userId, string itemId, bool? likes)
+ private UserItemDataDto UpdateUserItemRating(Guid userId, Guid itemId, bool? likes)
{
var user = _userManager.GetUserById(userId);
- var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
+ var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
data.Likes = likes;
- _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
+ _userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
return _userDataRepository.GetUserDataDto(item, user);
}
diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
index 096157e47..5e9270e0b 100644
--- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
@@ -24,10 +24,11 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
- [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
+ [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IncludeExternalContent { get; set; }
+ public bool IncludeHidden { get; set; }
public string PresetViews { get; set; }
}
@@ -40,7 +41,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
public class UserViewsService : BaseApiService
@@ -49,16 +50,18 @@ namespace MediaBrowser.Api.UserLibrary
private readonly IUserViewManager _userViewManager;
private readonly IDtoService _dtoService;
private readonly IAuthorizationContext _authContext;
+ private readonly ILibraryManager _libraryManager;
- public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext)
+ public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext, ILibraryManager libraryManager)
{
_userManager = userManager;
_userViewManager = userViewManager;
_dtoService = dtoService;
_authContext = authContext;
+ _libraryManager = libraryManager;
}
- public async Task<object> Get(GetUserViews request)
+ public object Get(GetUserViews request)
{
var query = new UserViewQuery
{
@@ -69,6 +72,7 @@ namespace MediaBrowser.Api.UserLibrary
{
query.IncludeExternalContent = request.IncludeExternalContent.Value;
}
+ query.IncludeHidden = request.IncludeHidden;
if (!string.IsNullOrWhiteSpace(request.PresetViews))
{
@@ -78,18 +82,16 @@ namespace MediaBrowser.Api.UserLibrary
var app = _authContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
{
- query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
+ query.PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows };
}
- //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
- var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
+ var folders = _userViewManager.GetUserViews(query);
var dtoOptions = GetDtoOptions(_authContext, request);
var fields = dtoOptions.Fields.ToList();
fields.Add(ItemFields.PrimaryImageAspectRatio);
fields.Add(ItemFields.DisplayPreferencesId);
- fields.Remove(ItemFields.SyncInfo);
fields.Remove(ItemFields.BasicSyncInfo);
dtoOptions.Fields = fields.ToArray(fields.Count);
@@ -111,7 +113,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var list = user.RootFolder
+ var list = _libraryManager.GetUserRootFolder()
.GetChildren(user, true)
.OfType<Folder>()
.Where(UserView.IsEligibleForGrouping)
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index db622a9b3..30ac88e00 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -7,6 +7,7 @@ using MediaBrowser.Model.Dto;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Services;
+using System;
namespace MediaBrowser.Api.UserLibrary
{
@@ -36,7 +37,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
}
/// <summary>
@@ -54,7 +55,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetItem(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -68,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
- if (!string.IsNullOrWhiteSpace(request.UserId))
+ if (!request.UserId.Equals(Guid.Empty))
{
var user = UserManager.GetUserById(request.UserId);
@@ -87,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var result = GetResult(request);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
/// <summary>
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 66d6a024e..29f3070a5 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
+using MediaBrowser.Controller.Authentication;
namespace MediaBrowser.Api
{
@@ -51,22 +52,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- /// <summary>
- /// Class GetUser
- /// </summary>
- [Route("/Users/{Id}/Offline", "GET", Summary = "Gets an offline user record by Id")]
- [Authenticated]
- public class GetOfflineUser : IReturn<UserDto>
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -81,7 +67,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -95,7 +81,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
[ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Pw { get; set; }
@@ -130,9 +116,6 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Pw { get; set; }
-
- [ApiMember(Name = "PasswordMd5", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public string PasswordMd5 { get; set; }
}
/// <summary>
@@ -146,7 +129,7 @@ namespace MediaBrowser.Api
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- public string Id { get; set; }
+ public Guid Id { get; set; }
/// <summary>
/// Gets or sets the password.
@@ -156,12 +139,6 @@ namespace MediaBrowser.Api
public string CurrentPw { get; set; }
- /// <summary>
- /// Gets or sets the new password.
- /// </summary>
- /// <value>The new password.</value>
- public string NewPassword { get; set; }
-
public string NewPw { get; set; }
/// <summary>
@@ -182,7 +159,7 @@ namespace MediaBrowser.Api
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
- public string Id { get; set; }
+ public Guid Id { get; set; }
/// <summary>
/// Gets or sets the new password.
@@ -216,7 +193,7 @@ namespace MediaBrowser.Api
public class UpdateUserPolicy : UserPolicy, IReturnVoid
{
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -227,7 +204,7 @@ namespace MediaBrowser.Api
public class UpdateUserConfiguration : UserConfiguration, IReturnVoid
{
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
+ public Guid Id { get; set; }
}
/// <summary>
@@ -296,7 +273,7 @@ namespace MediaBrowser.Api
IsHidden = false,
IsDisabled = false
- }, true);
+ }, true, true);
}
/// <summary>
@@ -306,10 +283,10 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetUsers request)
{
- return Get(request, false);
+ return Get(request, false, false);
}
- private object Get(GetUsers request, bool filterByDevice)
+ private object Get(GetUsers request, bool filterByDevice, bool filterByNetwork)
{
var users = _userManager.Users;
@@ -334,7 +311,15 @@ namespace MediaBrowser.Api
if (!string.IsNullOrWhiteSpace(deviceId))
{
- users = users.Where(i => _deviceManager.CanAccessDevice(i.Id.ToString("N"), deviceId));
+ users = users.Where(i => _deviceManager.CanAccessDevice(i, deviceId));
+ }
+ }
+
+ if (filterByNetwork)
+ {
+ if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
+ {
+ users = users.Where(i => i.Policy.EnableRemoteAccess);
}
}
@@ -365,32 +350,16 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
- public object Get(GetOfflineUser request)
- {
- var user = _userManager.GetUserById(request.Id);
-
- if (user == null)
- {
- throw new ResourceNotFoundException("User not found");
- }
-
- var result = _userManager.GetOfflineUserDto(user);
-
- return ToOptimizedResult(result);
- }
-
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(DeleteUser request)
+ public Task Delete(DeleteUser request)
{
- var task = DeleteAsync(request);
-
- Task.WaitAll(task);
+ return DeleteAsync(request);
}
- public async Task DeleteAsync(DeleteUser request)
+ public Task DeleteAsync(DeleteUser request)
{
var user = _userManager.GetUserById(request.Id);
@@ -399,9 +368,9 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException("User not found");
}
- _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), null);
+ _sessionMananger.RevokeUserTokens(user.Id, null);
- await _userManager.DeleteUser(user).ConfigureAwait(false);
+ return _userManager.DeleteUser(user);
}
/// <summary>
@@ -437,7 +406,6 @@ namespace MediaBrowser.Api
DeviceName = auth.Device,
Password = request.Pw,
PasswordSha1 = request.Password,
- PasswordMd5 = request.PasswordMd5,
RemoteEndPoint = Request.RemoteIp,
Username = request.Username
@@ -450,10 +418,9 @@ namespace MediaBrowser.Api
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdateUserPassword request)
+ public Task Post(UpdateUserPassword request)
{
- var task = PostAsync(request);
- Task.WaitAll(task);
+ return PostAsync(request);
}
public async Task PostAsync(UpdateUserPassword request)
@@ -469,22 +436,22 @@ namespace MediaBrowser.Api
if (request.ResetPassword)
{
- _userManager.ResetPassword(user);
+ await _userManager.ResetPassword(user).ConfigureAwait(false);
}
else
{
- var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, null, Request.RemoteIp, false).ConfigureAwait(false);
+ var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false);
if (success == null)
{
throw new ArgumentException("Invalid user or password entered.");
}
- _userManager.ChangePassword(user, request.NewPw, request.NewPassword);
+ await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
- _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken);
+ _sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
}
@@ -513,11 +480,11 @@ namespace MediaBrowser.Api
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdateUser request)
+ public async Task Post(UpdateUser request)
{
var id = GetPathValue(1);
- AssertCanUpdateUser(_authContext, _userManager, id, false);
+ AssertCanUpdateUser(_authContext, _userManager, new Guid(id), false);
var dtoUser = request;
@@ -526,15 +493,14 @@ namespace MediaBrowser.Api
if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal))
{
_userManager.UpdateUser(user);
+ _userManager.UpdateConfiguration(user, dtoUser.Configuration);
}
else
{
- var task = _userManager.RenameUser(user, dtoUser.Name);
+ await _userManager.RenameUser(user, dtoUser.Name).ConfigureAwait(false);
- Task.WaitAll(task);
+ _userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration);
}
-
- _userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration);
}
/// <summary>
@@ -542,11 +508,11 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Post(CreateUserByName request)
+ public async Task<object> Post(CreateUserByName request)
{
var dtoUser = request;
- var newUser = _userManager.CreateUser(dtoUser.Name).Result;
+ var newUser = await _userManager.CreateUser(dtoUser.Name).ConfigureAwait(false);
var result = _userManager.GetUserDto(newUser, Request.RemoteIp);
@@ -558,16 +524,20 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Post(ForgotPassword request)
+ public async Task<object> Post(ForgotPassword request)
{
var isLocal = Request.IsLocal || _networkManager.IsInLocalNetwork(Request.RemoteIp);
- return _userManager.StartForgotPasswordProcess(request.EnteredUsername, isLocal);
+ var result = await _userManager.StartForgotPasswordProcess(request.EnteredUsername, isLocal).ConfigureAwait(false);
+
+ return result;
}
- public object Post(ForgotPasswordPin request)
+ public async Task<object> Post(ForgotPasswordPin request)
{
- return _userManager.RedeemPasswordResetPin(request.Pin);
+ var result = await _userManager.RedeemPasswordResetPin(request.Pin).ConfigureAwait(false);
+
+ return result;
}
public void Post(UpdateUserConfiguration request)
@@ -606,7 +576,7 @@ namespace MediaBrowser.Api
}
var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
- _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken);
+ _sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
_userManager.UpdateUserPolicy(request.Id, request);
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index 3f4bb46f4..40d2e066c 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Api
public class GetAdditionalParts : IReturn<QueryResult<BaseItemDto>>
{
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
+ public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the id.
@@ -76,11 +76,11 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetAdditionalParts request)
{
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id)
- ? (!string.IsNullOrWhiteSpace(request.UserId)
- ? user.RootFolder
+ ? (!request.UserId.Equals(Guid.Empty)
+ ? _libraryManager.GetUserRootFolder()
: _libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
@@ -105,7 +105,7 @@ namespace MediaBrowser.Api
TotalRecordCount = items.Length
};
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
public void Delete(DeleteAlternateSources request)
@@ -115,12 +115,12 @@ namespace MediaBrowser.Api
foreach (var link in video.GetLinkedAlternateVersions())
{
link.SetPrimaryVersionId(null);
- link.LinkedAlternateVersions = Video.EmptyLinkedChildArray;
+ link.LinkedAlternateVersions = Array.Empty<LinkedChild>();
link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
}
- video.LinkedAlternateVersions = Video.EmptyLinkedChildArray;
+ video.LinkedAlternateVersions = Array.Empty<LinkedChild>();
video.SetPrimaryVersionId(null);
video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
}
@@ -140,11 +140,6 @@ namespace MediaBrowser.Api
var videosWithVersions = items.Where(i => i.MediaSourceCount > 1)
.ToList();
- if (videosWithVersions.Count > 1)
- {
- throw new ArgumentException("Videos with sub-versions cannot be merged.");
- }
-
var primaryVersion = videosWithVersions.FirstOrDefault();
if (primaryVersion == null)
@@ -185,10 +180,23 @@ namespace MediaBrowser.Api
Path = item.Path,
ItemId = item.Id
});
+
+ foreach (var linkedItem in item.LinkedAlternateVersions)
+ {
+ if (!list.Any(i => string.Equals(i.Path, linkedItem.Path, StringComparison.OrdinalIgnoreCase)))
+ {
+ list.Add(linkedItem);
+ }
+ }
+
+ if (item.LinkedAlternateVersions.Length > 0)
+ {
+ item.LinkedAlternateVersions = Array.Empty<LinkedChild>();
+ item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ }
}
primaryVersion.LinkedAlternateVersions = list.ToArray();
-
primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
}
}
diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
deleted file mode 100644
index 310e2aa63..000000000
--- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-namespace MediaBrowser.Common.Configuration
-{
- public class ConfigurationUpdateEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
- /// <summary>
- /// Gets or sets the new configuration.
- /// </summary>
- /// <value>The new configuration.</value>
- public object NewConfiguration { get; set; }
- }
-}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
deleted file mode 100644
index d2446ce46..000000000
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-
-namespace MediaBrowser.Common.Configuration
-{
- /// <summary>
- /// Interface IApplicationPaths
- /// </summary>
- public interface IApplicationPaths
- {
- /// <summary>
- /// Gets the path to the program data folder
- /// </summary>
- /// <value>The program data path.</value>
- string ProgramDataPath { get; }
-
- /// <summary>
- /// Gets the path to the program system folder
- /// </summary>
- /// <value>The program data path.</value>
- string ProgramSystemPath { get; }
-
- /// <summary>
- /// Gets the folder path to the data directory
- /// </summary>
- /// <value>The data directory.</value>
- string DataPath { get; }
-
- /// <summary>
- /// Gets the image cache path.
- /// </summary>
- /// <value>The image cache path.</value>
- string ImageCachePath { get; }
-
- /// <summary>
- /// Gets the path to the plugin directory
- /// </summary>
- /// <value>The plugins path.</value>
- string PluginsPath { get; }
-
- /// <summary>
- /// Gets the path to the plugin configurations directory
- /// </summary>
- /// <value>The plugin configurations path.</value>
- string PluginConfigurationsPath { get; }
-
- /// <summary>
- /// Gets the path to where temporary update files will be stored
- /// </summary>
- /// <value>The plugin configurations path.</value>
- string TempUpdatePath { get; }
-
- /// <summary>
- /// Gets the path to the log directory
- /// </summary>
- /// <value>The log directory path.</value>
- string LogDirectoryPath { get; }
-
- /// <summary>
- /// Gets the path to the application configuration root directory
- /// </summary>
- /// <value>The configuration directory path.</value>
- string ConfigurationDirectoryPath { get; }
-
- /// <summary>
- /// Gets the path to the system configuration file
- /// </summary>
- /// <value>The system configuration file path.</value>
- string SystemConfigurationFilePath { get; }
-
- /// <summary>
- /// Gets the folder path to the cache directory
- /// </summary>
- /// <value>The cache directory.</value>
- string CachePath { get; }
-
- /// <summary>
- /// Gets the folder path to the temp directory within the cache folder
- /// </summary>
- /// <value>The temp directory.</value>
- string TempDirectory { get; }
- }
-
-}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
deleted file mode 100644
index 6ed638536..000000000
--- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Common.Configuration
-{
- public interface IConfigurationFactory
- {
- IEnumerable<ConfigurationStore> GetConfigurations();
- }
-
- public class ConfigurationStore
- {
- public string Key { get; set; }
-
- public Type ConfigurationType { get; set; }
- }
-
- public interface IValidatingConfiguration
- {
- void Validate(object oldConfig, object newConfig);
- }
-}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
deleted file mode 100644
index d826a3ee7..000000000
--- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using MediaBrowser.Model.Configuration;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Common.Configuration
-{
- public interface IConfigurationManager
- {
- /// <summary>
- /// Occurs when [configuration updating].
- /// </summary>
- event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
-
- /// <summary>
- /// Occurs when [configuration updated].
- /// </summary>
- event EventHandler<EventArgs> ConfigurationUpdated;
-
- /// <summary>
- /// Occurs when [named configuration updated].
- /// </summary>
- event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
-
- /// <summary>
- /// Gets or sets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
- IApplicationPaths CommonApplicationPaths { get; }
-
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- /// <value>The configuration.</value>
- BaseApplicationConfiguration CommonConfiguration { get; }
-
- /// <summary>
- /// Saves the configuration.
- /// </summary>
- void SaveConfiguration();
-
- /// <summary>
- /// Replaces the configuration.
- /// </summary>
- /// <param name="newConfiguration">The new configuration.</param>
- void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration);
-
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>System.Object.</returns>
- object GetConfiguration(string key);
-
- /// <summary>
- /// Gets the type of the configuration.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>Type.</returns>
- Type GetConfigurationType(string key);
-
- /// <summary>
- /// Saves the configuration.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="configuration">The configuration.</param>
- void SaveConfiguration(string key, object configuration);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="factories">The factories.</param>
- void AddParts(IEnumerable<IConfigurationFactory> factories);
- }
-
- public static class ConfigurationManagerExtensions
- {
- public static T GetConfiguration<T>(this IConfigurationManager manager, string key)
- {
- return (T)manager.GetConfiguration(key);
- }
- }
-}
diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs
deleted file mode 100644
index 2bb52f0ae..000000000
--- a/MediaBrowser.Common/Events/EventHelper.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Events
-{
- /// <summary>
- /// Class EventHelper
- /// </summary>
- public static class EventHelper
- {
- /// <summary>
- /// Fires the event.
- /// </summary>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
- /// <param name="logger">The logger.</param>
- public static void QueueEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger)
- {
- if (handler != null)
- {
- Task.Run(() =>
- {
- try
- {
- handler(sender, args);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error in event handler", ex);
- }
- });
- }
- }
-
- /// <summary>
- /// Queues the event.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="args">The args.</param>
- /// <param name="logger">The logger.</param>
- public static void QueueEventIfNotNull<T>(EventHandler<T> handler, object sender, T args, ILogger logger)
- {
- if (handler != null)
- {
- Task.Run(() =>
- {
- try
- {
- handler(sender, args);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error in event handler", ex);
- }
- });
- }
- }
-
- /// <summary>
- /// Fires the event.
- /// </summary>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
- /// <param name="logger">The logger.</param>
- public static void FireEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger)
- {
- if (handler != null)
- {
- try
- {
- handler(sender, args);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error in event handler", ex);
- }
- }
- }
-
- /// <summary>
- /// Fires the event.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="args">The args.</param>
- /// <param name="logger">The logger.</param>
- public static void FireEventIfNotNull<T>(EventHandler<T> handler, object sender, T args, ILogger logger)
- {
- if (handler != null)
- {
- try
- {
- handler(sender, args);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error in event handler", ex);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
deleted file mode 100644
index d7f4424fa..000000000
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text.RegularExpressions;
-using MediaBrowser.Model.Cryptography;
-
-namespace MediaBrowser.Common.Extensions
-{
- /// <summary>
- /// Class BaseExtensions
- /// </summary>
- public static class BaseExtensions
- {
- public static ICryptoProvider CryptographyProvider { get; set; }
-
- /// <summary>
- /// Strips the HTML.
- /// </summary>
- /// <param name="htmlString">The HTML string.</param>
- /// <returns>System.String.</returns>
- public static string StripHtml(this string htmlString)
- {
- // http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
- const string pattern = @"<(.|\n)*?>";
-
- return Regex.Replace(htmlString, pattern, string.Empty).Trim();
- }
-
- /// <summary>
- /// Gets the M d5.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <returns>Guid.</returns>
- public static Guid GetMD5(this string str)
- {
- return CryptographyProvider.GetMD5(str);
- }
-
- /// <summary>
- /// Gets the MB id.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="type">The type.</param>
- /// <returns>Guid.</returns>
- /// <exception cref="System.ArgumentNullException">type</exception>
- [Obsolete("Use LibraryManager.GetNewItemId")]
- public static Guid GetMBId(this string str, Type type)
- {
- if (type == null)
- {
- throw new ArgumentNullException("type");
- }
-
- var key = type.FullName + str.ToLower();
-
- return key.GetMD5();
- }
- }
-}
diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
deleted file mode 100644
index 89e20b1b4..000000000
--- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-
-namespace MediaBrowser.Common.Extensions
-{
- /// <summary>
- /// Class ResourceNotFoundException
- /// </summary>
- public class ResourceNotFoundException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
- /// </summary>
- public ResourceNotFoundException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public ResourceNotFoundException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RemoteServiceUnavailableException : Exception
- {
- public RemoteServiceUnavailableException()
- {
-
- }
-
- public RemoteServiceUnavailableException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RateLimitExceededException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- public RateLimitExceededException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public RateLimitExceededException(string message)
- : base(message)
- {
-
- }
- }
-}
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
deleted file mode 100644
index dc0c9ac9b..000000000
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common
-{
- /// <summary>
- /// An interface to be implemented by the applications hosting a kernel
- /// </summary>
- public interface IApplicationHost
- {
- /// <summary>
- /// Gets the display name of the operating system.
- /// </summary>
- /// <value>The display name of the operating system.</value>
- string OperatingSystemDisplayName { get; }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- string SystemId { get; }
-
- /// <summary>
- /// Occurs when [application updated].
- /// </summary>
- event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated;
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has pending kernel reload.
- /// </summary>
- /// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value>
- bool HasPendingRestart { get; }
-
- bool IsShuttingDown { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance can self restart.
- /// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- bool CanSelfRestart { get; }
-
- /// <summary>
- /// Occurs when [has pending restart changed].
- /// </summary>
- event EventHandler HasPendingRestartChanged;
-
- /// <summary>
- /// Notifies the pending restart.
- /// </summary>
- void NotifyPendingRestart();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- void Restart();
-
- /// <summary>
- /// Gets the application version.
- /// </summary>
- /// <value>The application version.</value>
- Version ApplicationVersion { get; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- bool CanSelfUpdate { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is first run.
- /// </summary>
- /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
- bool IsFirstRun { get; }
-
- /// <summary>
- /// Gets the failed assemblies.
- /// </summary>
- /// <value>The failed assemblies.</value>
- List<string> FailedAssemblies { get; }
-
- /// <summary>
- /// Gets all concrete types.
- /// </summary>
- /// <value>All concrete types.</value>
- Type[] AllConcreteTypes { get; }
-
- /// <summary>
- /// Gets the exports.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param>
- /// <returns>IEnumerable{``0}.</returns>
- IEnumerable<T> GetExports<T>(bool manageLiftime = true);
-
- /// <summary>
- /// Checks for update.
- /// </summary>
- /// <returns>Task{CheckForUpdateResult}.</returns>
- Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress);
-
- /// <summary>
- /// Updates the application.
- /// </summary>
- /// <returns>Task.</returns>
- Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress);
-
- /// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- T Resolve<T>();
-
- /// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- T TryResolve<T>();
-
- /// <summary>
- /// Shuts down.
- /// </summary>
- Task Shutdown();
-
- /// <summary>
- /// Gets the plugins.
- /// </summary>
- /// <value>The plugins.</value>
- IPlugin[] Plugins { get; }
-
- /// <summary>
- /// Removes the plugin.
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- void RemovePlugin(IPlugin plugin);
-
- /// <summary>
- /// Inits this instance.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- Task Init(IProgress<double> progress);
-
- /// <summary>
- /// Creates the instance.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns>System.Object.</returns>
- object CreateInstance(Type type);
-
- PackageVersionClass SystemUpdateLevel { get; }
- }
-}
diff --git a/MediaBrowser.Common/IDependencyContainer.cs b/MediaBrowser.Common/IDependencyContainer.cs
deleted file mode 100644
index 423c1740a..000000000
--- a/MediaBrowser.Common/IDependencyContainer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace MediaBrowser.Common
-{
- public interface IDependencyContainer
- {
- void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
- where T : class;
-
- void RegisterSingleInstance<T>(Func<T> func)
- where T : class;
-
- void Register(Type typeInterface, Type typeImplementation);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
deleted file mode 100644
index d561b634e..000000000
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ /dev/null
@@ -1,90 +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>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Common</RootNamespace>
- <AssemblyName>MediaBrowser.Common</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProductVersion>10.0.0</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- </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>
- <PlatformTarget>AnyCPU</PlatformTarget>
- </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="Configuration\ConfigurationUpdateEventArgs.cs" />
- <Compile Include="Configuration\IConfigurationManager.cs" />
- <Compile Include="Configuration\IConfigurationFactory.cs" />
- <Compile Include="Events\EventHelper.cs" />
- <Compile Include="Extensions\BaseExtensions.cs" />
- <Compile Include="Extensions\ResourceNotFoundException.cs" />
- <Compile Include="IDependencyContainer.cs" />
- <Compile Include="Configuration\IApplicationPaths.cs" />
- <Compile Include="Net\HttpRequestOptions.cs" />
- <Compile Include="Net\HttpResponseInfo.cs" />
- <Compile Include="IApplicationHost.cs" />
- <Compile Include="Net\IHttpClient.cs" />
- <Compile Include="Net\INetworkManager.cs" />
- <Compile Include="Plugins\IDependencyModule.cs" />
- <Compile Include="Plugins\IPlugin.cs" />
- <Compile Include="Progress\ActionableProgress.cs" />
- <Compile Include="Plugins\BasePlugin.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Security\IRequiresRegistration.cs" />
- <Compile Include="Security\ISecurityManager.cs" />
- <Compile Include="Security\PaymentRequiredException.cs" />
- <Compile Include="Updates\GithubUpdater.cs" />
- <Compile Include="Updates\IInstallationManager.cs" />
- <Compile Include="Updates\InstallationEventArgs.cs" />
- <Compile Include="Updates\InstallationFailedEventArgs.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup />
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
- <!-- 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.Common/MediaBrowser.Common.nuget.targets b/MediaBrowser.Common/MediaBrowser.Common.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.Common/MediaBrowser.Common.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.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
deleted file mode 100644
index 8f0b155f3..000000000
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Text;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpRequestOptions
- /// </summary>
- public class HttpRequestOptions
- {
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- public CompressionMethod? DecompressionMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the accept header.
- /// </summary>
- /// <value>The accept header.</value>
- public string AcceptHeader
- {
- get { return GetHeaderValue("Accept"); }
- set
- {
- RequestHeaders["Accept"] = value;
- }
- }
- /// <summary>
- /// Gets or sets the cancellation token.
- /// </summary>
- /// <value>The cancellation token.</value>
- public CancellationToken CancellationToken { get; set; }
-
- /// <summary>
- /// Gets or sets the resource pool.
- /// </summary>
- /// <value>The resource pool.</value>
- public SemaphoreSlim ResourcePool { get; set; }
-
- /// <summary>
- /// Gets or sets the user agent.
- /// </summary>
- /// <value>The user agent.</value>
- public string UserAgent
- {
- get { return GetHeaderValue("User-Agent"); }
- set
- {
- RequestHeaders["User-Agent"] = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the referrer.
- /// </summary>
- /// <value>The referrer.</value>
- public string Referer { get; set; }
-
- /// <summary>
- /// Gets or sets the host.
- /// </summary>
- /// <value>The host.</value>
- public string Host { get; set; }
-
- /// <summary>
- /// Gets or sets the progress.
- /// </summary>
- /// <value>The progress.</value>
- public IProgress<double> Progress { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable HTTP compression].
- /// </summary>
- /// <value><c>true</c> if [enable HTTP compression]; otherwise, <c>false</c>.</value>
- public bool EnableHttpCompression { get; set; }
-
- public Dictionary<string, string> RequestHeaders { get; private set; }
-
- public string RequestContentType { get; set; }
-
- public string RequestContent { get; set; }
- public byte[] RequestContentBytes { get; set; }
-
- public bool BufferContent { get; set; }
-
- public bool LogRequest { get; set; }
- public bool LogRequestAsDebug { get; set; }
- public bool LogErrors { get; set; }
- public bool LogResponse { get; set; }
- public bool LogResponseHeaders { get; set; }
-
- public bool LogErrorResponseBody { get; set; }
- public bool EnableKeepAlive { get; set; }
-
- public CacheMode CacheMode { get; set; }
- public TimeSpan CacheLength { get; set; }
-
- public int TimeoutMs { get; set; }
- public bool EnableDefaultUserAgent { get; set; }
-
- public bool AppendCharsetToMimeType { get; set; }
-
- private string GetHeaderValue(string name)
- {
- string value;
-
- RequestHeaders.TryGetValue(name, out value);
-
- return value;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
- /// </summary>
- public HttpRequestOptions()
- {
- EnableHttpCompression = true;
-
- RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- LogRequest = true;
- LogErrors = true;
- CacheMode = CacheMode.None;
-
- TimeoutMs = 20000;
- }
-
- public void SetPostData(IDictionary<string,string> values)
- {
- var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key]));
- var postContent = string.Join("&", strings.ToArray());
-
- RequestContent = postContent;
- RequestContentType = "application/x-www-form-urlencoded";
- }
- }
-
- public enum CacheMode
- {
- None = 0,
- Unconditional = 1
- }
-
- public enum CompressionMethod
- {
- Deflate,
- Gzip
- }
-}
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
deleted file mode 100644
index 0d7fb69cb..000000000
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpResponseInfo
- /// </summary>
- public class HttpResponseInfo : IDisposable
- {
- /// <summary>
- /// Gets or sets the type of the content.
- /// </summary>
- /// <value>The type of the content.</value>
- public string ContentType { get; set; }
-
- /// <summary>
- /// Gets or sets the response URL.
- /// </summary>
- /// <value>The response URL.</value>
- public string ResponseUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- /// <value>The content.</value>
- public Stream Content { get; set; }
-
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode StatusCode { get; set; }
-
- /// <summary>
- /// Gets or sets the temp file path.
- /// </summary>
- /// <value>The temp file path.</value>
- public string TempFilePath { get; set; }
-
- /// <summary>
- /// Gets or sets the length of the content.
- /// </summary>
- /// <value>The length of the content.</value>
- public long? ContentLength { get; set; }
-
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public Dictionary<string,string> Headers { get; set; }
-
- private readonly IDisposable _disposable;
-
- public HttpResponseInfo(IDisposable disposable)
- {
- _disposable = disposable;
- Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- public HttpResponseInfo()
- {
- Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
- public void Dispose()
- {
- if (_disposable != null)
- {
- _disposable.Dispose();
- }
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
deleted file mode 100644
index cf5511965..000000000
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Interface IHttpClient
- /// </summary>
- public interface IHttpClient
- {
- /// <summary>
- /// Gets the response.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
-
- /// <summary>
- /// Gets the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{Stream}.</returns>
- Task<Stream> Get(HttpRequestOptions options);
-
- /// <summary>
- /// Sends the asynchronous.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
-
- /// <summary>
- /// Posts the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> Post(HttpRequestOptions options);
-
- /// <summary>
- /// Downloads the contents of a given url into a temporary location
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{System.String}.</returns>
- /// <exception cref="System.ArgumentNullException">progress</exception>
- /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
- Task<string> GetTempFile(HttpRequestOptions options);
-
- /// <summary>
- /// Gets the temporary file response.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
deleted file mode 100644
index 6ddc2e799..000000000
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
-using System.Collections.Generic;
-using System;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Net
-{
- public interface INetworkManager
- {
- event EventHandler NetworkChanged;
-
- /// <summary>
- /// Gets a random port number that is currently available
- /// </summary>
- /// <returns>System.Int32.</returns>
- int GetRandomUnusedTcpPort();
-
- int GetRandomUnusedUdpPort();
-
- /// <summary>
- /// Returns MAC Address from first Network Card in Computer
- /// </summary>
- /// <returns>[string] MAC Address</returns>
- string GetMacAddress();
-
- /// <summary>
- /// Determines whether [is in private address space] [the specified endpoint].
- /// </summary>
- /// <param name="endpoint">The endpoint.</param>
- /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns>
- bool IsInPrivateAddressSpace(string endpoint);
-
- /// <summary>
- /// Gets the network shares.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{NetworkShare}.</returns>
- IEnumerable<NetworkShare> GetNetworkShares(string path);
-
- /// <summary>
- /// Gets available devices within the domain
- /// </summary>
- /// <returns>PC's in the Domain</returns>
- IEnumerable<FileSystemEntryInfo> GetNetworkDevices();
-
- /// <summary>
- /// Determines whether [is in local network] [the specified endpoint].
- /// </summary>
- /// <param name="endpoint">The endpoint.</param>
- /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
- bool IsInLocalNetwork(string endpoint);
-
- List<IpAddressInfo> GetLocalIpAddresses();
-
- IpAddressInfo ParseIpAddress(string ipAddress);
-
- bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo);
-
- Task<IpAddressInfo[]> GetHostAddressesAsync(string host);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
deleted file mode 100644
index 73be04ac8..000000000
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ /dev/null
@@ -1,294 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Common.Plugins
-{
- /// <summary>
- /// Provides a common base class for all plugins
- /// </summary>
- /// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
- public abstract class BasePlugin<TConfigurationType> : IPlugin, IPluginAssembly
- where TConfigurationType : BasePluginConfiguration
- {
- /// <summary>
- /// Gets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
- protected IApplicationPaths ApplicationPaths { get; private set; }
-
- /// <summary>
- /// Gets the XML serializer.
- /// </summary>
- /// <value>The XML serializer.</value>
- protected IXmlSerializer XmlSerializer { get; private set; }
-
- /// <summary>
- /// Gets the name of the plugin
- /// </summary>
- /// <value>The name.</value>
- public abstract string Name { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is first run.
- /// </summary>
- /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
- public bool IsFirstRun { get; private set; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public virtual string Description
- {
- get { return string.Empty; }
- }
-
- /// <summary>
- /// Gets the type of configuration this plugin uses
- /// </summary>
- /// <value>The type of the configuration.</value>
- public Type ConfigurationType
- {
- get { return typeof(TConfigurationType); }
- }
-
- public void SetAttributes(string assemblyFilePath, string assemblyFileName, Version assemblyVersion)
- {
- AssemblyFilePath = assemblyFilePath;
- AssemblyFileName = assemblyFileName;
- Version = assemblyVersion;
- }
-
- public void SetId(Guid assemblyId)
- {
- Id = assemblyId;
- }
-
- private Func<string, DateTime> _dateModifiedFn;
- private Action<string> _directoryCreateFn;
- public void SetStartupInfo(bool isFirstRun, Func<string, DateTime> dateModifiedFn, Action<string> directoryCreateFn)
- {
- IsFirstRun = isFirstRun;
-
- // hack alert, until the .net core transition is complete
- _dateModifiedFn = dateModifiedFn;
- _directoryCreateFn = directoryCreateFn;
- }
-
- /// <summary>
- /// Gets the unique id.
- /// </summary>
- /// <value>The unique id.</value>
- public virtual Guid Id { get; private set; }
-
- /// <summary>
- /// Gets the plugin version
- /// </summary>
- /// <value>The version.</value>
- public Version Version { get; private set; }
-
- /// <summary>
- /// Gets the name the assembly file
- /// </summary>
- /// <value>The name of the assembly file.</value>
- protected string AssemblyFileName { get; private set; }
-
- /// <summary>
- /// Gets the last date modified of the configuration
- /// </summary>
- /// <value>The configuration date last modified.</value>
- public DateTime ConfigurationDateLastModified
- {
- get
- {
- // Ensure it's been lazy loaded
- var config = Configuration;
-
- return _dateModifiedFn(ConfigurationFilePath);
- }
- }
-
- /// <summary>
- /// Gets the path to the assembly file
- /// </summary>
- /// <value>The assembly file path.</value>
- public string AssemblyFilePath { get; private set; }
-
- /// <summary>
- /// The _configuration sync lock
- /// </summary>
- private readonly object _configurationSyncLock = new object();
- /// <summary>
- /// The _configuration
- /// </summary>
- private TConfigurationType _configuration;
- /// <summary>
- /// Gets the plugin's configuration
- /// </summary>
- /// <value>The configuration.</value>
- public TConfigurationType Configuration
- {
- get
- {
- // Lazy load
- if (_configuration == null)
- {
- lock (_configurationSyncLock)
- {
- if (_configuration == null)
- {
- _configuration = LoadConfiguration();
- }
- }
- }
- return _configuration;
- }
- protected set
- {
- _configuration = value;
- }
- }
-
- private TConfigurationType LoadConfiguration()
- {
- var path = ConfigurationFilePath;
-
- try
- {
- return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
- }
- catch
- {
- return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
- }
- }
-
- /// <summary>
- /// Gets the name of the configuration file. Subclasses should override
- /// </summary>
- /// <value>The name of the configuration file.</value>
- public virtual string ConfigurationFileName
- {
- get { return Path.ChangeExtension(AssemblyFileName, ".xml"); }
- }
-
- /// <summary>
- /// Gets the full path to the configuration file
- /// </summary>
- /// <value>The configuration file path.</value>
- public string ConfigurationFilePath
- {
- get
- {
- return Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
- }
- }
-
- /// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
- /// </summary>
- /// <value>The data folder path.</value>
- public string DataFolderPath
- {
- get
- {
- // Give the folder name the same name as the config file name
- // We can always make this configurable if/when needed
- return Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(ConfigurationFileName));
- }
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
- /// </summary>
- /// <param name="applicationPaths">The application paths.</param>
- /// <param name="xmlSerializer">The XML serializer.</param>
- protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
- {
- ApplicationPaths = applicationPaths;
- XmlSerializer = xmlSerializer;
- }
-
- /// <summary>
- /// The _save lock
- /// </summary>
- private readonly object _configurationSaveLock = new object();
-
- /// <summary>
- /// Saves the current configuration to the file system
- /// </summary>
- public virtual void SaveConfiguration()
- {
- lock (_configurationSaveLock)
- {
- _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath));
-
- XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);
- }
- }
-
- /// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="System.ArgumentNullException">configuration</exception>
- public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
- {
- if (configuration == null)
- {
- throw new ArgumentNullException("configuration");
- }
-
- Configuration = (TConfigurationType)configuration;
-
- SaveConfiguration();
- }
-
- /// <summary>
- /// Gets the plugin info.
- /// </summary>
- /// <returns>PluginInfo.</returns>
- public PluginInfo GetPluginInfo()
- {
- var info = new PluginInfo
- {
- Name = Name,
- Version = Version.ToString(),
- AssemblyFileName = AssemblyFileName,
- ConfigurationDateLastModified = ConfigurationDateLastModified,
- Description = Description,
- Id = Id.ToString(),
- ConfigurationFileName = ConfigurationFileName
- };
-
- return info;
- }
-
- /// <summary>
- /// Called when just before the plugin is uninstalled from the server.
- /// </summary>
- public virtual void OnUninstalling()
- {
-
- }
-
- /// <summary>
- /// Gets the plugin's configuration
- /// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration IPlugin.Configuration
- {
- get { return Configuration; }
- }
- }
-
- public interface IPluginAssembly
- {
- void SetAttributes(string assemblyFilePath, string assemblyFileName, Version assemblyVersion);
- void SetId(Guid assemblyId);
- }
-}
diff --git a/MediaBrowser.Common/Plugins/IDependencyModule.cs b/MediaBrowser.Common/Plugins/IDependencyModule.cs
deleted file mode 100644
index 37ebe0d5b..000000000
--- a/MediaBrowser.Common/Plugins/IDependencyModule.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Common.Plugins
-{
- public interface IDependencyModule
- {
- void BindDependencies(IDependencyContainer container);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
deleted file mode 100644
index 999a783fd..000000000
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using MediaBrowser.Model.Plugins;
-using System;
-
-namespace MediaBrowser.Common.Plugins
-{
- /// <summary>
- /// Interface IPlugin
- /// </summary>
- public interface IPlugin
- {
- /// <summary>
- /// Gets the name of the plugin
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- string Description { get; }
-
- /// <summary>
- /// Gets the type of configuration this plugin uses
- /// </summary>
- /// <value>The type of the configuration.</value>
- Type ConfigurationType { get; }
-
- /// <summary>
- /// Gets the unique id.
- /// </summary>
- /// <value>The unique id.</value>
- Guid Id { get; }
-
- /// <summary>
- /// Gets the plugin version
- /// </summary>
- /// <value>The version.</value>
- Version Version { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is first run.
- /// </summary>
- /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
- bool IsFirstRun { get; }
-
- /// <summary>
- /// Gets the last date modified of the configuration
- /// </summary>
- /// <value>The configuration date last modified.</value>
- DateTime ConfigurationDateLastModified { get; }
-
- /// <summary>
- /// Gets the path to the assembly file
- /// </summary>
- /// <value>The assembly file path.</value>
- string AssemblyFilePath { get; }
-
- /// <summary>
- /// Gets the plugin's configuration
- /// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration Configuration { get; }
-
- /// <summary>
- /// Gets the name of the configuration file. Subclasses should override
- /// </summary>
- /// <value>The name of the configuration file.</value>
- string ConfigurationFileName { get; }
-
- /// <summary>
- /// Gets the full path to the configuration file
- /// </summary>
- /// <value>The configuration file path.</value>
- string ConfigurationFilePath { get; }
-
- /// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
- /// </summary>
- /// <value>The data folder path.</value>
- string DataFolderPath { get; }
-
- /// <summary>
- /// Saves the current configuration to the file system
- /// </summary>
- /// <exception cref="System.InvalidOperationException">Cannot call Plugin.SaveConfiguration from the UI.</exception>
- void SaveConfiguration();
-
- /// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="System.ArgumentNullException">configuration</exception>
- void UpdateConfiguration(BasePluginConfiguration configuration);
-
- /// <summary>
- /// Gets the plugin info.
- /// </summary>
- /// <returns>PluginInfo.</returns>
- PluginInfo GetPluginInfo();
-
- /// <summary>
- /// Called when just before the plugin is uninstalled from the server.
- /// </summary>
- void OnUninstalling();
-
- void SetStartupInfo(bool isFirstRun, Func<string, DateTime> dateModifiedFn, Action<string> directoryCreateFn);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs
deleted file mode 100644
index 5b318c6a7..000000000
--- a/MediaBrowser.Common/Progress/ActionableProgress.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Common.Progress
-{
- /// <summary>
- /// Class ActionableProgress
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class ActionableProgress<T> : IProgress<T>, IDisposable
- {
- /// <summary>
- /// The _actions
- /// </summary>
- private readonly List<Action<T>> _actions = new List<Action<T>>();
- public event EventHandler<T> ProgressChanged;
-
- /// <summary>
- /// Registers the action.
- /// </summary>
- /// <param name="action">The action.</param>
- public void RegisterAction(Action<T> action)
- {
- _actions.Add(action);
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _actions.Clear();
- }
- }
-
- public void Report(T value)
- {
- if (ProgressChanged != null)
- {
- ProgressChanged(this, value);
- }
-
- foreach (var action in _actions)
- {
- action(value);
- }
- }
- }
-
- public class SimpleProgress<T> : IProgress<T>
- {
- public event EventHandler<T> ProgressChanged;
-
- public void Report(T value)
- {
- if (ProgressChanged != null)
- {
- ProgressChanged(this, value);
- }
- }
- }
-}
diff --git a/MediaBrowser.Common/Properties/AssemblyInfo.cs b/MediaBrowser.Common/Properties/AssemblyInfo.cs
deleted file mode 100644
index 09fd68f93..000000000
--- a/MediaBrowser.Common/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,27 +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.Common")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Common")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[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)]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-// \ No newline at end of file
diff --git a/MediaBrowser.Common/Security/IRequiresRegistration.cs b/MediaBrowser.Common/Security/IRequiresRegistration.cs
deleted file mode 100644
index 7b1667c2e..000000000
--- a/MediaBrowser.Common/Security/IRequiresRegistration.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Security
-{
- public interface IRequiresRegistration
- {
- /// <summary>
- /// Load all registration information required for this entity.
- /// Your class should re-load all MBRegistrationRecords when this is called even if they were
- /// previously loaded.
- /// </summary>
- /// <returns></returns>
- Task LoadRegistrationInfoAsync();
- }
-}
diff --git a/MediaBrowser.Common/Security/ISecurityManager.cs b/MediaBrowser.Common/Security/ISecurityManager.cs
deleted file mode 100644
index b47511c33..000000000
--- a/MediaBrowser.Common/Security/ISecurityManager.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Security
-{
- public interface ISecurityManager
- {
- /// <summary>
- /// Gets a value indicating whether this instance is MB supporter.
- /// </summary>
- /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value>
- bool IsMBSupporter { get; }
-
- /// <summary>
- /// Gets or sets the supporter key.
- /// </summary>
- /// <value>The supporter key.</value>
- string SupporterKey { get; set; }
-
- /// <summary>
- /// Gets the registration status. Overload to support existing plug-ins.
- /// </summary>
- /// <param name="feature">The feature.</param>
- /// <param name="mb2Equivalent">The MB2 equivalent.</param>
- /// <returns>Task{MBRegistrationRecord}.</returns>
- Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent = null);
-
- /// <summary>
- /// Gets the registration status.
- /// </summary>
- /// <param name="feature">The feature.</param>
- /// <param name="mb2Equivalent">The MB2 equivalent.</param>
- /// <param name="version">The version of the feature</param>
- /// <returns>Task{MBRegistrationRecord}.</returns>
- Task<MBRegistrationRecord> GetRegistrationStatus(string feature, string mb2Equivalent, string version);
-
- /// <summary>
- /// Load all registration info for all entities that require registration
- /// </summary>
- /// <returns></returns>
- Task LoadAllRegistrationInfo();
-
- /// <summary>
- /// Register and app store sale with our back-end
- /// </summary>
- /// <param name="parameters">Json parameters to pass to admin server</param>
- Task RegisterAppStoreSale(string parameters);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Security/PaymentRequiredException.cs b/MediaBrowser.Common/Security/PaymentRequiredException.cs
deleted file mode 100644
index 27b3e6961..000000000
--- a/MediaBrowser.Common/Security/PaymentRequiredException.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System;
-
-namespace MediaBrowser.Common.Security
-{
- public class PaymentRequiredException : Exception
- {
- }
-}
diff --git a/MediaBrowser.Common/Updates/GithubUpdater.cs b/MediaBrowser.Common/Updates/GithubUpdater.cs
deleted file mode 100644
index 4275799a9..000000000
--- a/MediaBrowser.Common/Updates/GithubUpdater.cs
+++ /dev/null
@@ -1,278 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Updates;
-
-namespace MediaBrowser.Common.Updates
-{
- public class GithubUpdater
- {
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
-
- public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer)
- {
- _httpClient = httpClient;
- _jsonSerializer = jsonSerializer;
- }
-
- public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, TimeSpan cacheLength, CancellationToken cancellationToken)
- {
- var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
-
- var options = new HttpRequestOptions
- {
- Url = url,
- EnableKeepAlive = false,
- CancellationToken = cancellationToken,
- UserAgent = "Emby/3.0",
- BufferContent = false
- };
-
- if (cacheLength.Ticks > 0)
- {
- options.CacheMode = CacheMode.Unconditional;
- options.CacheLength = cacheLength;
- }
-
- using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
- {
- using (var stream = response.Content)
- {
- var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
-
- return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
- }
- }
- }
-
- private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
- {
- if (updateLevel == PackageVersionClass.Release)
- {
- // Technically all we need to do is check that it's not pre-release
- // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly.
- obj = obj.Where(i => !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) && !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
- }
- else if (updateLevel == PackageVersionClass.Beta)
- {
- obj = obj.Where(i => i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
- }
- else if (updateLevel == PackageVersionClass.Dev)
- {
- obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- var availableUpdate = obj
- .Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename))
- .Where(i => i != null)
- .OrderByDescending(i => Version.Parse(i.AvailableVersion))
- .FirstOrDefault();
-
- return availableUpdate ?? new CheckForUpdateResult
- {
- IsUpdateAvailable = false
- };
- }
-
- private bool MatchesUpdateLevel(RootObject i, PackageVersionClass updateLevel)
- {
- if (updateLevel == PackageVersionClass.Beta)
- {
- return i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase);
- }
- if (updateLevel == PackageVersionClass.Dev)
- {
- return !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) ||
- i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase);
- }
-
- // Technically all we need to do is check that it's not pre-release
- // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly.
- return !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) &&
- !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase);
- }
-
- public async Task<List<RootObject>> GetLatestReleases(string organzation, string repository, string assetFilename, CancellationToken cancellationToken)
- {
- var list = new List<RootObject>();
-
- var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
-
- var options = new HttpRequestOptions
- {
- Url = url,
- EnableKeepAlive = false,
- CancellationToken = cancellationToken,
- UserAgent = "Emby/3.0",
- BufferContent = false
- };
-
- using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
- {
- using (var stream = response.Content)
- {
- var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
-
- obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray();
-
- list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
- list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
- list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Dev)).OrderByDescending(GetVersion).Take(1));
-
- return list;
- }
- }
- }
-
- public Version GetVersion(RootObject obj)
- {
- Version version;
- if (!Version.TryParse(obj.tag_name, out version))
- {
- return new Version(1, 0);
- }
-
- return version;
- }
-
- private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
- {
- Version version;
- var versionString = obj.tag_name;
- if (!Version.TryParse(versionString, out version))
- {
- return null;
- }
-
- if (version < minVersion)
- {
- return null;
- }
-
- var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename, versionString));
-
- if (asset == null)
- {
- return null;
- }
-
- return new CheckForUpdateResult
- {
- AvailableVersion = version.ToString(),
- IsUpdateAvailable = version > minVersion,
- Package = new PackageVersionInfo
- {
- classification = obj.prerelease ?
- (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
- PackageVersionClass.Release,
- name = packageName,
- sourceUrl = asset.browser_download_url,
- targetFilename = targetFilename,
- versionStr = version.ToString(),
- requiredVersionStr = "1.0.0",
- description = obj.body,
- infoUrl = obj.html_url
- }
- };
- }
-
- private bool IsAsset(Asset asset, string assetFilename, string version)
- {
- var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
-
- assetFilename = assetFilename.Replace("{version}", version);
-
- if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
- {
- return true;
- }
-
- return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase);
- }
-
- public class Uploader
- {
- public string login { get; set; }
- public int id { get; set; }
- public string avatar_url { get; set; }
- public string gravatar_id { get; set; }
- public string url { get; set; }
- public string html_url { get; set; }
- public string followers_url { get; set; }
- public string following_url { get; set; }
- public string gists_url { get; set; }
- public string starred_url { get; set; }
- public string subscriptions_url { get; set; }
- public string organizations_url { get; set; }
- public string repos_url { get; set; }
- public string events_url { get; set; }
- public string received_events_url { get; set; }
- public string type { get; set; }
- public bool site_admin { get; set; }
- }
-
- public class Asset
- {
- public string url { get; set; }
- public int id { get; set; }
- public string name { get; set; }
- public object label { get; set; }
- public Uploader uploader { get; set; }
- public string content_type { get; set; }
- public string state { get; set; }
- public int size { get; set; }
- public int download_count { get; set; }
- public string created_at { get; set; }
- public string updated_at { get; set; }
- public string browser_download_url { get; set; }
- }
-
- public class Author
- {
- public string login { get; set; }
- public int id { get; set; }
- public string avatar_url { get; set; }
- public string gravatar_id { get; set; }
- public string url { get; set; }
- public string html_url { get; set; }
- public string followers_url { get; set; }
- public string following_url { get; set; }
- public string gists_url { get; set; }
- public string starred_url { get; set; }
- public string subscriptions_url { get; set; }
- public string organizations_url { get; set; }
- public string repos_url { get; set; }
- public string events_url { get; set; }
- public string received_events_url { get; set; }
- public string type { get; set; }
- public bool site_admin { get; set; }
- }
-
- public class RootObject
- {
- public string url { get; set; }
- public string assets_url { get; set; }
- public string upload_url { get; set; }
- public string html_url { get; set; }
- public int id { get; set; }
- public string tag_name { get; set; }
- public string target_commitish { get; set; }
- public string name { get; set; }
- public bool draft { get; set; }
- public Author author { get; set; }
- public bool prerelease { get; set; }
- public string created_at { get; set; }
- public string published_at { get; set; }
- public List<Asset> assets { get; set; }
- public string tarball_url { get; set; }
- public string zipball_url { get; set; }
- public string body { get; set; }
- }
- }
-}
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
deleted file mode 100644
index dab38b27c..000000000
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Updates
-{
- public interface IInstallationManager : IDisposable
- {
- event EventHandler<InstallationEventArgs> PackageInstalling;
- event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
- event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
- event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
-
- /// <summary>
- /// The current installations
- /// </summary>
- List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
-
- /// <summary>
- /// The completed installations
- /// </summary>
- IEnumerable<InstallationInfo> CompletedInstallations { get; }
-
- /// <summary>
- /// Occurs when [plugin uninstalled].
- /// </summary>
- event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
-
- /// <summary>
- /// Occurs when [plugin updated].
- /// </summary>
- event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated;
-
- /// <summary>
- /// Occurs when [plugin updated].
- /// </summary>
- event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
-
- /// <summary>
- /// Gets all available packages.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
- /// <param name="packageType">Type of the package.</param>
- /// <param name="applicationVersion">The application version.</param>
- /// <returns>Task{List{PackageInfo}}.</returns>
- Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
- bool withRegistration = true,
- string packageType = null,
- Version applicationVersion = null);
-
- /// <summary>
- /// Gets all available packages from a static resource.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{List{PackageInfo}}.</returns>
- Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the package.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="guid">The assembly guid</param>
- /// <param name="classification">The classification.</param>
- /// <param name="version">The version.</param>
- /// <returns>Task{PackageVersionInfo}.</returns>
- Task<PackageVersionInfo> GetPackage(string name, string guid, PackageVersionClass classification, Version version);
-
- /// <summary>
- /// Gets the latest compatible version.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="guid">The assembly guid</param>
- /// <param name="currentServerVersion">The current server version.</param>
- /// <param name="classification">The classification.</param>
- /// <returns>Task{PackageVersionInfo}.</returns>
- Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release);
-
- /// <summary>
- /// Gets the latest compatible version.
- /// </summary>
- /// <param name="availablePackages">The available packages.</param>
- /// <param name="name">The name.</param>
- /// <param name="guid">The assembly guid</param>
- /// <param name="currentServerVersion">The current server version.</param>
- /// <param name="classification">The classification.</param>
- /// <returns>PackageVersionInfo.</returns>
- PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release);
-
- /// <summary>
- /// Gets the available plugin updates.
- /// </summary>
- /// <param name="applicationVersion">The current server version.</param>
- /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns>
- Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken);
-
- /// <summary>
- /// Installs the package.
- /// </summary>
- /// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">package</exception>
- Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
-
- /// <summary>
- /// Uninstalls a plugin
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- /// <exception cref="System.ArgumentException"></exception>
- void UninstallPlugin(IPlugin plugin);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
deleted file mode 100644
index 9dc8ead83..000000000
--- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using MediaBrowser.Model.Updates;
-
-namespace MediaBrowser.Common.Updates
-{
- public class InstallationEventArgs
- {
- public InstallationInfo InstallationInfo { get; set; }
-
- public PackageVersionInfo PackageVersionInfo { get; set; }
- }
-}
diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
deleted file mode 100644
index 69dc1ee98..000000000
--- a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace MediaBrowser.Common.Updates
-{
- public class InstallationFailedEventArgs : InstallationEventArgs
- {
- public Exception Exception { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
deleted file mode 100644
index 54faa1443..000000000
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Progress;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class Channel : Folder
- {
- public override bool IsVisible(User user)
- {
- if (user.Policy.BlockedChannels != null)
- {
- if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
- else
- {
- if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- return base.IsVisible(user);
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.Channel; }
- }
-
- protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
- {
- try
- {
- // Don't blow up here because it could cause parent screens with other content to fail
- return ChannelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = Id.ToString("N"),
- Limit = query.Limit,
- StartIndex = query.StartIndex,
- UserId = query.User.Id.ToString("N"),
- OrderBy = query.OrderBy
-
- }, new SimpleProgress<double>(), CancellationToken.None).Result;
- }
- catch
- {
- // Already logged at lower levels
- return new QueryResult<BaseItem>();
- }
- }
-
- protected override string GetInternalMetadataPath(string basePath)
- {
- return GetInternalMetadataPath(basePath, Id);
- }
-
- public static string GetInternalMetadataPath(string basePath, Guid id)
- {
- return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata");
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- protected override bool IsAllowTagFilterEnforced()
- {
- return false;
- }
-
- internal static bool IsChannelVisible(BaseItem channelItem, User user)
- {
- var channel = ChannelManager.GetChannel(channelItem.ChannelId);
-
- return channel.IsVisible(user);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs
deleted file mode 100644
index 57c2f1f7f..000000000
--- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class ChannelItemInfo : IHasProviderIds
- {
- public string Name { get; set; }
-
- public string SeriesName { get; set; }
-
- public string Id { get; set; }
-
- public ChannelItemType Type { get; set; }
-
- public string OfficialRating { get; set; }
-
- public string Overview { get; set; }
-
- public List<string> Genres { get; set; }
- public List<string> Studios { get; set; }
- public List<string> Tags { get; set; }
-
- public List<PersonInfo> People { get; set; }
-
- public float? CommunityRating { get; set; }
-
- public long? RunTimeTicks { get; set; }
-
- public string ImageUrl { get; set; }
-
- public ChannelMediaType MediaType { get; set; }
- public ChannelFolderType FolderType { get; set; }
-
- public ChannelMediaContentType ContentType { get; set; }
- public ExtraType ExtraType { get; set; }
- public List<TrailerType> TrailerTypes { get; set; }
-
- public Dictionary<string, string> ProviderIds { get; set; }
-
- public DateTime? PremiereDate { get; set; }
- public int? ProductionYear { get; set; }
-
- public DateTime? DateCreated { get; set; }
-
- public int? IndexNumber { get; set; }
- public int? ParentIndexNumber { get; set; }
-
- public List<ChannelMediaInfo> MediaSources { get; set; }
-
- public bool IsInfiniteStream { get; set; }
-
- public string HomePageUrl { get; set; }
-
- public List<string> Artists { get; set; }
-
- public List<string> AlbumArtists { get; set; }
-
- public ChannelItemInfo()
- {
- MediaSources = new List<ChannelMediaInfo>();
- TrailerTypes = new List<TrailerType>();
- Genres = new List<string>();
- Studios = new List<string>();
- People = new List<PersonInfo>();
- Tags = new List<string>();
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- Artists = new List<string>();
- AlbumArtists = new List<string>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs
deleted file mode 100644
index f88881811..000000000
--- a/MediaBrowser.Controller/Channels/ChannelItemResult.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class ChannelItemResult
- {
- public List<ChannelItemInfo> Items { get; set; }
-
- public int? TotalRecordCount { get; set; }
-
- public ChannelItemResult()
- {
- Items = new List<ChannelItemInfo>();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ChannelItemType.cs b/MediaBrowser.Controller/Channels/ChannelItemType.cs
deleted file mode 100644
index 184ce8a76..000000000
--- a/MediaBrowser.Controller/Channels/ChannelItemType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Channels
-{
- public enum ChannelItemType
- {
- Media = 0,
-
- Folder = 1
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs
deleted file mode 100644
index cf7b6ba6a..000000000
--- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class ChannelMediaInfo
- {
- public string Path { get; set; }
-
- public Dictionary<string, string> RequiredHttpHeaders { get; set; }
-
- public string Container { get; set; }
- public string AudioCodec { get; set; }
- public string VideoCodec { get; set; }
-
- public int? AudioBitrate { get; set; }
- public int? VideoBitrate { get; set; }
- public int? Width { get; set; }
- public int? Height { get; set; }
- public int? AudioChannels { get; set; }
- public int? AudioSampleRate { get; set; }
-
- public string VideoProfile { get; set; }
- public float? VideoLevel { get; set; }
- public float? Framerate { get; set; }
-
- public bool? IsAnamorphic { get; set; }
-
- public MediaProtocol Protocol { get; set; }
-
- public long? RunTimeTicks { get; set; }
-
- public string Id { get; set; }
-
- public bool ReadAtNativeFramerate { get; set; }
- public bool SupportsDirectPlay { get; set; }
-
- public ChannelMediaInfo()
- {
- RequiredHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- // This is most common
- Protocol = MediaProtocol.Http;
- SupportsDirectPlay = true;
- }
-
- public MediaSourceInfo ToMediaSource(Guid itemId)
- {
- var id = string.IsNullOrWhiteSpace(Path) ?
- itemId.ToString("N") :
- Path.GetMD5().ToString("N");
-
- var source = new MediaSourceInfo
- {
- MediaStreams = GetMediaStreams(this).ToList(),
-
- Container = Container,
- Protocol = Protocol,
- Path = Path,
- RequiredHttpHeaders = RequiredHttpHeaders,
- RunTimeTicks = RunTimeTicks,
- Name = id,
- Id = id,
- ReadAtNativeFramerate = ReadAtNativeFramerate,
- SupportsDirectStream = Protocol == MediaProtocol.Http && !string.IsNullOrWhiteSpace(Container) && !string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase),
- SupportsDirectPlay = SupportsDirectPlay,
- IsRemote = true
- };
-
- source.InferTotalBitrate();
-
- return source;
- }
-
- private IEnumerable<MediaStream> GetMediaStreams(ChannelMediaInfo info)
- {
- var list = new List<MediaStream>();
-
- if (!string.IsNullOrWhiteSpace(info.VideoCodec))
- {
- list.Add(new MediaStream
- {
- Type = MediaStreamType.Video,
- Width = info.Width,
- RealFrameRate = info.Framerate,
- Profile = info.VideoProfile,
- Level = info.VideoLevel,
- Index = -1,
- Height = info.Height,
- Codec = info.VideoCodec,
- BitRate = info.VideoBitrate,
- AverageFrameRate = info.Framerate
- });
- }
-
- if (!string.IsNullOrWhiteSpace(info.AudioCodec))
- {
- list.Add(new MediaStream
- {
- Type = MediaStreamType.Audio,
- Index = -1,
- Codec = info.AudioCodec,
- BitRate = info.AudioBitrate,
- Channels = info.AudioChannels,
- SampleRate = info.AudioSampleRate
- });
- }
-
- return list;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ChannelParentalRating.cs b/MediaBrowser.Controller/Channels/ChannelParentalRating.cs
deleted file mode 100644
index d9cc521b3..000000000
--- a/MediaBrowser.Controller/Channels/ChannelParentalRating.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace MediaBrowser.Controller.Channels
-{
- public enum ChannelParentalRating
- {
- GeneralAudience = 0,
-
- UsPG = 1,
-
- UsPG13 = 2,
-
- UsR = 3,
-
- Adult = 4
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
deleted file mode 100644
index c2a51654c..000000000
--- a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace MediaBrowser.Controller.Channels
-{
- public class ChannelSearchInfo
- {
- public string SearchTerm { get; set; }
-
- public string UserId { get; set; }
- }
-
- public class ChannelLatestMediaSearch
- {
- public string UserId { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs
deleted file mode 100644
index dc1d9b00a..000000000
--- a/MediaBrowser.Controller/Channels/IChannel.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface IChannel
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- string Description { get; }
-
- /// <summary>
- /// Gets the data version.
- /// </summary>
- /// <value>The data version.</value>
- string DataVersion { get; }
-
- /// <summary>
- /// Gets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- string HomePageUrl { get; }
-
- /// <summary>
- /// Gets the parental rating.
- /// </summary>
- /// <value>The parental rating.</value>
- ChannelParentalRating ParentalRating { get; }
-
- /// <summary>
- /// Gets the channel information.
- /// </summary>
- /// <returns>ChannelFeatures.</returns>
- InternalChannelFeatures GetChannelFeatures();
-
- /// <summary>
- /// Determines whether [is enabled for] [the specified user].
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns><c>true</c> if [is enabled for] [the specified user]; otherwise, <c>false</c>.</returns>
- bool IsEnabledFor(string userId);
-
- /// <summary>
- /// Gets the channel items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ChannelItem}}.</returns>
- Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel image.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{DynamicImageInfo}.</returns>
- Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the supported channel images.
- /// </summary>
- /// <returns>IEnumerable{ImageType}.</returns>
- IEnumerable<ImageType> GetSupportedChannelImages();
- }
-}
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
deleted file mode 100644
index 37fc892b3..000000000
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface IChannelManager
- {
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="channels">The channels.</param>
- void AddParts(IEnumerable<IChannel> channels);
-
- /// <summary>
- /// Gets the channel features.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>ChannelFeatures.</returns>
- ChannelFeatures GetChannelFeatures(string id);
-
- bool SupportsSync(string channelId);
-
- /// <summary>
- /// Gets all channel features.
- /// </summary>
- /// <returns>IEnumerable{ChannelFeatures}.</returns>
- ChannelFeatures[] GetAllChannelFeatures();
-
- /// <summary>
- /// Gets the channel.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Channel.</returns>
- Channel GetChannel(string id);
-
- /// <summary>
- /// Gets the channels internal.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;QueryResult&lt;Channel&gt;&gt;.</returns>
- Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken 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);
-
- /// <summary>
- /// Gets all media internal.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;QueryResult&lt;BaseItem&gt;&gt;.</returns>
- Task<QueryResult<BaseItem>> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets all media.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the latest media.
- /// </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);
-
- /// <summary>
- /// Gets the latest channel items internal.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;QueryResult&lt;BaseItem&gt;&gt;.</returns>
- Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(AllChannelMediaQuery query, CancellationToken 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);
-
- /// <summary>
- /// Gets the channel items internal.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;QueryResult&lt;BaseItem&gt;&gt;.</returns>
- Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, IProgress<double> progress, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel item media sources.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
- IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel folder.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>BaseItemDto.</returns>
- Folder GetInternalChannelFolder(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel folder.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- BaseItemDto GetChannelFolder(string userId, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Channels/IHasCacheKey.cs b/MediaBrowser.Controller/Channels/IHasCacheKey.cs
deleted file mode 100644
index 6376d2f91..000000000
--- a/MediaBrowser.Controller/Channels/IHasCacheKey.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface IHasCacheKey
- {
- /// <summary>
- /// Gets the cache key.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>System.String.</returns>
- string GetCacheKey(string userId);
- }
-}
diff --git a/MediaBrowser.Controller/Channels/IIndexableChannel.cs b/MediaBrowser.Controller/Channels/IIndexableChannel.cs
deleted file mode 100644
index 0b52585e8..000000000
--- a/MediaBrowser.Controller/Channels/IIndexableChannel.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface IIndexableChannel
- {
- /// <summary>
- /// Gets all media.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ChannelItemResult}.</returns>
- Task<ChannelItemResult> GetAllMedia(InternalAllChannelMediaQuery query, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
deleted file mode 100644
index b4b6be9ba..000000000
--- a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface IRequiresMediaInfoCallback
- {
- /// <summary>
- /// Gets the channel item media information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ChannelMediaInfo}}.</returns>
- Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
deleted file mode 100644
index d63708494..000000000
--- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Channels
-{
- public interface ISearchableChannel
- {
- /// <summary>
- /// Searches the specified search term.
- /// </summary>
- /// <param name="searchInfo">The search information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
- Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
- }
-
- public interface ISupportsLatestMedia
- {
- /// <summary>
- /// Gets the latest media.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
- Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/InternalAllChannelMediaQuery.cs b/MediaBrowser.Controller/Channels/InternalAllChannelMediaQuery.cs
deleted file mode 100644
index ce091da7e..000000000
--- a/MediaBrowser.Controller/Channels/InternalAllChannelMediaQuery.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class InternalAllChannelMediaQuery
- {
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the content types.
- /// </summary>
- /// <value>The content types.</value>
- public ChannelMediaContentType[] ContentTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the extra types.
- /// </summary>
- /// <value>The extra types.</value>
- public ExtraType[] ExtraTypes { get; set; }
- public TrailerType[] TrailerTypes { get; set; }
-
- public InternalAllChannelMediaQuery()
- {
- ContentTypes = new ChannelMediaContentType[] { };
- ExtraTypes = new ExtraType[] { };
- TrailerTypes = new TrailerType[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
deleted file mode 100644
index 976808aad..000000000
--- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using MediaBrowser.Model.Channels;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class InternalChannelFeatures
- {
- /// <summary>
- /// Gets or sets the media types.
- /// </summary>
- /// <value>The media types.</value>
- public List<ChannelMediaType> MediaTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the content types.
- /// </summary>
- /// <value>The content types.</value>
- public List<ChannelMediaContentType> ContentTypes { get; set; }
-
- /// <summary>
- /// Represents the maximum number of records the channel allows retrieving at a time
- /// </summary>
- public int? MaxPageSize { get; set; }
-
- /// <summary>
- /// Gets or sets the default sort orders.
- /// </summary>
- /// <value>The default sort orders.</value>
- public List<ChannelItemSortField> DefaultSortFields { get; set; }
-
- /// <summary>
- /// Indicates if a sort ascending/descending toggle is supported or not.
- /// </summary>
- public bool SupportsSortOrderToggle { get; set; }
- /// <summary>
- /// Gets or sets the automatic refresh levels.
- /// </summary>
- /// <value>The automatic refresh levels.</value>
- public int? AutoRefreshLevels { get; set; }
-
- /// <summary>
- /// Gets or sets the daily download limit.
- /// </summary>
- /// <value>The daily download limit.</value>
- public int? DailyDownloadLimit { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [supports downloading].
- /// </summary>
- /// <value><c>true</c> if [supports downloading]; otherwise, <c>false</c>.</value>
- public bool SupportsContentDownloading { get; set; }
-
- public InternalChannelFeatures()
- {
- MediaTypes = new List<ChannelMediaType>();
- ContentTypes = new List<ChannelMediaContentType>();
-
- DefaultSortFields = new List<ChannelItemSortField>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs
deleted file mode 100644
index 82ef6b946..000000000
--- a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Channels;
-
-namespace MediaBrowser.Controller.Channels
-{
- public class InternalChannelItemQuery
- {
- public string FolderId { get; set; }
-
- public string UserId { get; set; }
-
- public int? StartIndex { get; set; }
-
- public int? Limit { get; set; }
-
- public ChannelItemSortField? SortBy { get; set; }
-
- public bool SortDescending { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
deleted file mode 100644
index d1c190ab5..000000000
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Chapters
-{
- /// <summary>
- /// Interface IChapterManager
- /// </summary>
- public interface IChapterManager
- {
- /// <summary>
- /// Gets the chapters.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <returns>List{ChapterInfo}.</returns>
- List<ChapterInfo> GetChapters(string itemId);
-
- /// <summary>
- /// Saves the chapters.
- /// </summary>
- void SaveChapters(string itemId, List<ChapterInfo> chapters);
- }
-}
diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
deleted file mode 100644
index 7a387e319..000000000
--- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Collections
-{
- public class CollectionCreationOptions : IHasProviderIds
- {
- public string Name { get; set; }
-
- public Guid? ParentId { get; set; }
-
- public bool IsLocked { get; set; }
-
- public Dictionary<string, string> ProviderIds { get; set; }
-
- public string[] ItemIdList { get; set; }
- public string[] UserIds { get; set; }
-
- public CollectionCreationOptions()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- ItemIdList = new string[] { };
- UserIds = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Controller/Collections/CollectionEvents.cs b/MediaBrowser.Controller/Collections/CollectionEvents.cs
deleted file mode 100644
index 80f66a444..000000000
--- a/MediaBrowser.Controller/Collections/CollectionEvents.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Collections
-{
- public class CollectionCreatedEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the collection.
- /// </summary>
- /// <value>The collection.</value>
- public BoxSet Collection { get; set; }
-
- /// <summary>
- /// Gets or sets the options.
- /// </summary>
- /// <value>The options.</value>
- public CollectionCreationOptions Options { get; set; }
- }
-
- public class CollectionModifiedEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the collection.
- /// </summary>
- /// <value>The collection.</value>
- public BoxSet Collection { get; set; }
-
- /// <summary>
- /// Gets or sets the items changed.
- /// </summary>
- /// <value>The items changed.</value>
- public List<BaseItem> ItemsChanged { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
deleted file mode 100644
index d89843cc0..000000000
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Collections
-{
- public interface ICollectionManager
- {
- /// <summary>
- /// Occurs when [collection created].
- /// </summary>
- event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
-
- /// <summary>
- /// Occurs when [items added to collection].
- /// </summary>
- event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
-
- /// <summary>
- /// Occurs when [items removed from collection].
- /// </summary>
- event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
-
- /// <summary>
- /// Creates the collection.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task.</returns>
- Task<BoxSet> CreateCollection(CollectionCreationOptions options);
-
- /// <summary>
- /// Adds to collection.
- /// </summary>
- /// <param name="collectionId">The collection identifier.</param>
- /// <param name="itemIds">The item ids.</param>
- /// <returns>Task.</returns>
- Task AddToCollection(Guid collectionId, IEnumerable<string> itemIds);
-
- /// <summary>
- /// Removes from collection.
- /// </summary>
- /// <param name="collectionId">The collection identifier.</param>
- /// <param name="itemIds">The item ids.</param>
- /// <returns>Task.</returns>
- Task RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds);
-
- Task AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
- Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
-
- /// <summary>
- /// Collapses the items within box sets.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{BaseItem}.</returns>
- IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user);
-
- /// <summary>
- /// Gets the collections folder.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Folder.</returns>
- Folder GetCollectionsFolder(string userId);
-
- /// <summary>
- /// Gets the collections.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable&lt;BoxSet&gt;.</returns>
- IEnumerable<BoxSet> GetCollections(User user);
- }
-}
diff --git a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs
deleted file mode 100644
index 38d2611f0..000000000
--- a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Collections
-{
- public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay
- {
- public ManualCollectionsFolder()
- {
- Name = "Collections";
- }
-
- public override bool IsHidden
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- public bool IsHiddenFromUser(User user)
- {
- return !ConfigurationManager.Configuration.DisplayCollectionsView;
- }
-
- [IgnoreDataMember]
- public override string CollectionType
- {
- get { return Model.Entities.CollectionType.BoxSets; }
- }
-
- public override string GetClientTypeName()
- {
- return typeof(CollectionFolder).Name;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs
deleted file mode 100644
index af5714932..000000000
--- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Controller.Configuration
-{
- /// <summary>
- /// Interface IServerConfigurationManager
- /// </summary>
- public interface IServerConfigurationManager : IConfigurationManager
- {
- /// <summary>
- /// Gets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
- IServerApplicationPaths ApplicationPaths { get; }
-
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- /// <value>The configuration.</value>
- ServerConfiguration Configuration { get; }
-
- bool SetOptimalValues();
- }
-}
diff --git a/MediaBrowser.Controller/Connect/ConnectSupporterSummary.cs b/MediaBrowser.Controller/Connect/ConnectSupporterSummary.cs
deleted file mode 100644
index 20eef0521..000000000
--- a/MediaBrowser.Controller/Connect/ConnectSupporterSummary.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Connect;
-using System.Collections.Generic;
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Controller.Connect
-{
- public class ConnectSupporterSummary
- {
- public int MaxUsers { get; set; }
- public List<ConnectUser> Users { get; set; }
- public List<UserDto> EligibleUsers { get; set; }
-
- public ConnectSupporterSummary()
- {
- Users = new List<ConnectUser>();
- EligibleUsers = new List<UserDto>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs
deleted file mode 100644
index 70bdc52e6..000000000
--- a/MediaBrowser.Controller/Connect/IConnectManager.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Connect;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Connect
-{
- public interface IConnectManager
- {
- /// <summary>
- /// Gets the wan API address.
- /// </summary>
- /// <value>The wan API address.</value>
- string WanApiAddress { get; }
-
- /// <summary>
- /// Links the user.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="connectUsername">The connect username.</param>
- /// <returns>Task.</returns>
- Task<UserLinkResult> LinkUser(string userId, string connectUsername);
-
- /// <summary>
- /// Removes the link.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Task.</returns>
- Task RemoveConnect(string userId);
-
- /// <summary>
- /// Invites the user.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;UserLinkResult&gt;.</returns>
- Task<UserLinkResult> InviteUser(ConnectAuthorizationRequest request);
-
- /// <summary>
- /// Gets the pending guests.
- /// </summary>
- /// <returns>Task&lt;List&lt;ConnectAuthorization&gt;&gt;.</returns>
- Task<List<ConnectAuthorization>> GetPendingGuests();
-
- /// <summary>
- /// Gets the user from exchange token.
- /// </summary>
- /// <param name="token">The token.</param>
- /// <returns>User.</returns>
- User GetUserFromExchangeToken(string token);
-
- /// <summary>
- /// Cancels the authorization.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelAuthorization(string id);
-
- /// <summary>
- /// Authenticates the specified username.
- /// </summary>
- Task<ConnectAuthenticationResult> Authenticate(string username, string password, string passwordMd5);
-
- /// <summary>
- /// Gets the local user.
- /// </summary>
- /// <param name="connectUserId">The connect user identifier.</param>
- /// <returns>Task&lt;User&gt;.</returns>
- Task<User> GetLocalUser(string connectUserId);
-
- /// <summary>
- /// Determines whether [is authorization token valid] [the specified token].
- /// </summary>
- /// <param name="token">The token.</param>
- /// <returns><c>true</c> if [is authorization token valid] [the specified token]; otherwise, <c>false</c>.</returns>
- bool IsAuthorizationTokenValid(string token);
- }
-}
diff --git a/MediaBrowser.Controller/Connect/UserLinkResult.cs b/MediaBrowser.Controller/Connect/UserLinkResult.cs
deleted file mode 100644
index 16ebfc70a..000000000
--- a/MediaBrowser.Controller/Connect/UserLinkResult.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Connect
-{
- public class UserLinkResult
- {
- public bool IsPending { get; set; }
- public bool IsNewUserInvitation { get; set; }
- public string GuestDisplayName { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
deleted file mode 100644
index b3f3bb902..000000000
--- a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MediaBrowser.Model.Devices;
-
-namespace MediaBrowser.Controller.Devices
-{
- public class CameraImageUploadInfo
- {
- public LocalFileInfo FileInfo { get; set; }
- public DeviceInfo Device { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
deleted file mode 100644
index 676db09aa..000000000
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Session;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Devices
-{
- public interface IDeviceManager
- {
- /// <summary>
- /// Occurs when [device options updated].
- /// </summary>
- event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
- /// <summary>
- /// Occurs when [camera image uploaded].
- /// </summary>
- event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
-
- /// <summary>
- /// Registers the device.
- /// </summary>
- /// <param name="reportedId">The reported identifier.</param>
- /// <param name="name">The name.</param>
- /// <param name="appName">Name of the application.</param>
- /// <param name="appVersion">The application version.</param>
- /// <param name="usedByUserId">The used by user identifier.</param>
- /// <returns>Task.</returns>
- DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId);
-
- /// <summary>
- /// Saves the capabilities.
- /// </summary>
- /// <param name="reportedId">The reported identifier.</param>
- /// <param name="capabilities">The capabilities.</param>
- /// <returns>Task.</returns>
- void SaveCapabilities(string reportedId, ClientCapabilities capabilities);
-
- /// <summary>
- /// Gets the capabilities.
- /// </summary>
- /// <param name="reportedId">The reported identifier.</param>
- /// <returns>ClientCapabilities.</returns>
- ClientCapabilities GetCapabilities(string reportedId);
-
- /// <summary>
- /// Gets the device information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>DeviceInfo.</returns>
- DeviceInfo GetDevice(string id);
-
- /// <summary>
- /// Updates the device information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="options">The options.</param>
- /// <returns>Task.</returns>
- void UpdateDeviceInfo(string id, DeviceOptions options);
-
- /// <summary>
- /// Gets the devices.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable&lt;DeviceInfo&gt;.</returns>
- QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
-
- void DeleteDevice(string id);
-
- /// <summary>
- /// Gets the upload history.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <returns>ContentUploadHistory.</returns>
- ContentUploadHistory GetCameraUploadHistory(string deviceId);
-
- /// <summary>
- /// Accepts the upload.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="stream">The stream.</param>
- /// <param name="file">The file.</param>
- /// <returns>Task.</returns>
- Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file);
-
- /// <summary>
- /// Determines whether this instance [can access device] the specified user identifier.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="deviceId">The device identifier.</param>
- /// <returns><c>true</c> if this instance [can access device] the specified user identifier; otherwise, <c>false</c>.</returns>
- bool CanAccessDevice(string userId, string deviceId);
- }
-}
diff --git a/MediaBrowser.Controller/Devices/IDeviceRepository.cs b/MediaBrowser.Controller/Devices/IDeviceRepository.cs
deleted file mode 100644
index b9ebbb6c7..000000000
--- a/MediaBrowser.Controller/Devices/IDeviceRepository.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Session;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Devices
-{
- public interface IDeviceRepository
- {
- /// <summary>
- /// Registers the device.
- /// </summary>
- /// <param name="device">The device.</param>
- /// <returns>Task.</returns>
- void SaveDevice(DeviceInfo device);
-
- /// <summary>
- /// Saves the capabilities.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="capabilities">The capabilities.</param>
- /// <returns>Task.</returns>
- void SaveCapabilities(string id, ClientCapabilities capabilities);
-
- /// <summary>
- /// Gets the capabilities.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>ClientCapabilities.</returns>
- ClientCapabilities GetCapabilities(string id);
-
- /// <summary>
- /// Gets the device information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>DeviceInfo.</returns>
- DeviceInfo GetDevice(string id);
-
- List<DeviceInfo> GetDevices();
-
- /// <summary>
- /// Deletes the device.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- void DeleteDevice(string id);
-
- /// <summary>
- /// Gets the upload history.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <returns>ContentUploadHistory.</returns>
- ContentUploadHistory GetCameraUploadHistory(string deviceId);
-
- /// <summary>
- /// Saves the camera upload history.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="file">The file.</param>
- void AddCameraUpload(string deviceId, LocalFileInfo file);
- }
-}
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
deleted file mode 100644
index 2f64cd194..000000000
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Model.Dlna;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Dlna
-{
- public interface IDlnaManager
- {
- /// <summary>
- /// Gets the profile infos.
- /// </summary>
- /// <returns>IEnumerable{DeviceProfileInfo}.</returns>
- IEnumerable<DeviceProfileInfo> GetProfileInfos();
-
- /// <summary>
- /// Gets the profile.
- /// </summary>
- /// <param name="headers">The headers.</param>
- /// <returns>DeviceProfile.</returns>
- DeviceProfile GetProfile(IDictionary<string,string> headers);
-
- /// <summary>
- /// Gets the default profile.
- /// </summary>
- /// <returns>DeviceProfile.</returns>
- DeviceProfile GetDefaultProfile();
-
- /// <summary>
- /// Creates the profile.
- /// </summary>
- /// <param name="profile">The profile.</param>
- void CreateProfile(DeviceProfile profile);
-
- /// <summary>
- /// Updates the profile.
- /// </summary>
- /// <param name="profile">The profile.</param>
- void UpdateProfile(DeviceProfile profile);
-
- /// <summary>
- /// Deletes the profile.
- /// </summary>
- /// <param name="id">The identifier.</param>
- void DeleteProfile(string id);
-
- /// <summary>
- /// Gets the profile.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>DeviceProfile.</returns>
- DeviceProfile GetProfile(string id);
-
- /// <summary>
- /// Gets the profile.
- /// </summary>
- /// <param name="deviceInfo">The device information.</param>
- /// <returns>DeviceProfile.</returns>
- DeviceProfile GetProfile(DeviceIdentification deviceInfo);
-
- /// <summary>
- /// Gets the server description XML.
- /// </summary>
- /// <param name="headers">The headers.</param>
- /// <param name="serverUuId">The server uu identifier.</param>
- /// <param name="serverAddress">The server address.</param>
- /// <returns>System.String.</returns>
- string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId, string serverAddress);
-
- /// <summary>
- /// Gets the icon.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>DlnaIconResponse.</returns>
- ImageStream GetIcon(string filename);
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
deleted file mode 100644
index 757448eb2..000000000
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System;
-using MediaBrowser.Model.Drawing;
-
-namespace MediaBrowser.Controller.Drawing
-{
- public interface IImageEncoder
- {
- /// <summary>
- /// Gets the supported input formats.
- /// </summary>
- /// <value>The supported input formats.</value>
- string[] SupportedInputFormats { get; }
- /// <summary>
- /// Gets the supported output formats.
- /// </summary>
- /// <value>The supported output formats.</value>
- ImageFormat[] SupportedOutputFormats { get; }
-
- /// <summary>
- /// Encodes the image.
- /// </summary>
- string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
-
- /// <summary>
- /// Creates the image collage.
- /// </summary>
- /// <param name="options">The options.</param>
- void CreateImageCollage(ImageCollageOptions options);
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports image collage creation].
- /// </summary>
- /// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
- bool SupportsImageCollageCreation { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports image encoding].
- /// </summary>
- /// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
- bool SupportsImageEncoding { get; }
-
- ImageSize GetImageSize(string path);
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
deleted file mode 100644
index 0bc92ac7e..000000000
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Drawing
-{
- /// <summary>
- /// Interface IImageProcessor
- /// </summary>
- public interface IImageProcessor
- {
- /// <summary>
- /// Gets the supported input formats.
- /// </summary>
- /// <value>The supported input formats.</value>
- string[] SupportedInputFormats { get; }
-
- /// <summary>
- /// Gets the image enhancers.
- /// </summary>
- /// <value>The image enhancers.</value>
- IImageEnhancer[] ImageEnhancers { get; }
-
- ImageSize GetImageSize(string path);
-
- /// <summary>
- /// Gets the size of the image.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>ImageSize.</returns>
- ImageSize GetImageSize(BaseItem item, ItemImageInfo info);
-
- ImageSize GetImageSize(BaseItem item, ItemImageInfo info, bool allowSlowMethods, bool updateItem);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="enhancers">The enhancers.</param>
- void AddParts(IEnumerable<IImageEnhancer> enhancers);
-
- /// <summary>
- /// Gets the supported enhancers.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>IEnumerable{IImageEnhancer}.</returns>
- List<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType);
-
- /// <summary>
- /// Gets the image cache tag.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <returns>Guid.</returns>
- string GetImageCacheTag(IHasMetadata item, ItemImageInfo image);
-
- /// <summary>
- /// Gets the image cache tag.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <param name="imageEnhancers">The image enhancers.</param>
- /// <returns>Guid.</returns>
- string GetImageCacheTag(IHasMetadata item, ItemImageInfo image, List<IImageEnhancer> imageEnhancers);
-
- /// <summary>
- /// Processes the image.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="toStream">To stream.</param>
- /// <returns>Task.</returns>
- Task ProcessImage(ImageProcessingOptions options, Stream toStream);
-
- /// <summary>
- /// Processes the image.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task.</returns>
- Task<Tuple<string, string, DateTime>> ProcessImage(ImageProcessingOptions options);
-
- /// <summary>
- /// Gets the enhanced image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{System.String}.</returns>
- Task<string> GetEnhancedImage(IHasMetadata item, ImageType imageType, int imageIndex);
-
- /// <summary>
- /// Gets the supported image output formats.
- /// </summary>
- /// <returns>ImageOutputFormat[].</returns>
- ImageFormat[] GetSupportedImageOutputFormats();
-
- /// <summary>
- /// Creates the image collage.
- /// </summary>
- /// <param name="options">The options.</param>
- void CreateImageCollage(ImageCollageOptions options);
-
- /// <summary>
- /// Gets a value indicating whether [supports image collage creation].
- /// </summary>
- /// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
- bool SupportsImageCollageCreation { get; }
-
- IImageEncoder ImageEncoder { get; set; }
-
- bool SupportsTransparency(string path);
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs
deleted file mode 100644
index 92a7f5ac9..000000000
--- a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Controller.Drawing
-{
- public class ImageCollageOptions
- {
- /// <summary>
- /// Gets or sets the input paths.
- /// </summary>
- /// <value>The input paths.</value>
- public string[] InputPaths { get; set; }
- /// <summary>
- /// Gets or sets the output path.
- /// </summary>
- /// <value>The output path.</value>
- public string OutputPath { get; set; }
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int Width { get; set; }
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int Height { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
deleted file mode 100644
index 9936b1036..000000000
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Drawing
-{
- public static class ImageHelper
- {
- public static ImageSize GetNewImageSize(ImageProcessingOptions options, ImageSize? originalImageSize)
- {
- if (originalImageSize.HasValue)
- {
- // Determine the output size based on incoming parameters
- var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width, options.Height, options.MaxWidth, options.MaxHeight);
-
- return newSize;
- }
- return GetSizeEstimate(options);
- }
-
- public static IImageProcessor ImageProcessor { get; set; }
-
- private static ImageSize GetSizeEstimate(ImageProcessingOptions options)
- {
- if (options.Width.HasValue && options.Height.HasValue)
- {
- return new ImageSize(options.Width.Value, options.Height.Value);
- }
-
- var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item);
-
- var width = options.Width ?? options.MaxWidth;
-
- if (width.HasValue)
- {
- var heightValue = width.Value / aspect;
- return new ImageSize(width.Value, heightValue);
- }
-
- var height = options.Height ?? options.MaxHeight ?? 200;
- var widthValue = aspect * height;
- return new ImageSize(widthValue, height);
- }
-
- private static double GetEstimatedAspectRatio(ImageType type, IHasMetadata item)
- {
- switch (type)
- {
- case ImageType.Art:
- case ImageType.Backdrop:
- case ImageType.Chapter:
- case ImageType.Screenshot:
- case ImageType.Thumb:
- return 1.78;
- case ImageType.Banner:
- return 5.4;
- case ImageType.Box:
- case ImageType.BoxRear:
- case ImageType.Disc:
- case ImageType.Menu:
- return 1;
- case ImageType.Logo:
- return 2.58;
- case ImageType.Primary:
- return item.GetDefaultPrimaryImageAspectRatio() ?? .667;
- default:
- return 1;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
deleted file mode 100644
index 26283b5ea..000000000
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Drawing;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Drawing
-{
- public class ImageProcessingOptions
- {
- public ImageProcessingOptions()
- {
- RequiresAutoOrientation = true;
- }
-
- public string ItemId { get; set; }
- public string ItemType { get; set; }
- public IHasMetadata Item { get; set; }
-
- public ItemImageInfo Image { get; set; }
-
- public int ImageIndex { get; set; }
-
- public bool CropWhiteSpace { get; set; }
-
- public int? Width { get; set; }
-
- public int? Height { get; set; }
-
- public int? MaxWidth { get; set; }
-
- public int? MaxHeight { get; set; }
-
- public int Quality { get; set; }
-
- public List<IImageEnhancer> Enhancers { get; set; }
-
- public ImageFormat[] SupportedOutputFormats { get; set; }
-
- public bool AddPlayedIndicator { get; set; }
-
- public int? UnplayedCount { get; set; }
- public int? Blur { get; set; }
-
- public double PercentPlayed { get; set; }
-
- public string BackgroundColor { get; set; }
- public string ForegroundLayer { get; set; }
- public bool RequiresAutoOrientation { get; set; }
-
- public bool HasDefaultOptions(string originalImagePath)
- {
- return HasDefaultOptionsWithoutSize(originalImagePath) &&
- !Width.HasValue &&
- !Height.HasValue &&
- !MaxWidth.HasValue &&
- !MaxHeight.HasValue;
- }
-
- public bool HasDefaultOptions(string originalImagePath, ImageSize size)
- {
- if (!HasDefaultOptionsWithoutSize(originalImagePath))
- {
- return false;
- }
-
- if (Width.HasValue && !size.Width.Equals(Width.Value))
- {
- return false;
- }
- if (Height.HasValue && !size.Height.Equals(Height.Value))
- {
- return false;
- }
- if (MaxWidth.HasValue && size.Width > MaxWidth.Value)
- {
- return false;
- }
- if (MaxHeight.HasValue && size.Height > MaxHeight.Value)
- {
- return false;
- }
-
- return true;
- }
-
- public bool HasDefaultOptionsWithoutSize(string originalImagePath)
- {
- return (Quality >= 90) &&
- IsFormatSupported(originalImagePath) &&
- !AddPlayedIndicator &&
- PercentPlayed.Equals(0) &&
- !UnplayedCount.HasValue &&
- !Blur.HasValue &&
- !CropWhiteSpace &&
- string.IsNullOrEmpty(BackgroundColor) &&
- string.IsNullOrEmpty(ForegroundLayer);
- }
-
- private bool IsFormatSupported(string originalImagePath)
- {
- var ext = Path.GetExtension(originalImagePath);
- return SupportedOutputFormats.Any(outputFormat => string.Equals(ext, "." + outputFormat, StringComparison.OrdinalIgnoreCase));
- }
- }
-}
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
deleted file mode 100644
index 5dfa94e1e..000000000
--- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Drawing
-{
- public static class ImageProcessorExtensions
- {
- public static string GetImageCacheTag(this IImageProcessor processor, IHasMetadata item, ImageType imageType)
- {
- return processor.GetImageCacheTag(item, imageType, 0);
- }
-
- public static string GetImageCacheTag(this IImageProcessor processor, IHasMetadata item, ImageType imageType, int imageIndex)
- {
- var imageInfo = item.GetImageInfo(imageType, imageIndex);
-
- if (imageInfo == null)
- {
- return null;
- }
-
- return processor.GetImageCacheTag(item, imageInfo);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Drawing/ImageStream.cs b/MediaBrowser.Controller/Drawing/ImageStream.cs
deleted file mode 100644
index b5e14eb6c..000000000
--- a/MediaBrowser.Controller/Drawing/ImageStream.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Controller.Drawing
-{
- public class ImageStream : IDisposable
- {
- /// <summary>
- /// Gets or sets the stream.
- /// </summary>
- /// <value>The stream.</value>
- public Stream Stream { get; set; }
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- public ImageFormat Format { get; set; }
-
- public void Dispose()
- {
- if (Stream != null)
- {
- Stream.Dispose();
- }
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs
deleted file mode 100644
index f05ae4e71..000000000
--- a/MediaBrowser.Controller/Dto/DtoOptions.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Dto
-{
- public class DtoOptions
- {
- private static readonly List<ItemFields> DefaultExcludedFields = new List<ItemFields>
- {
- ItemFields.SeasonUserData,
- ItemFields.RefreshState
- };
-
- public ItemFields[] Fields { get; set; }
- public ImageType[] ImageTypes { get; set; }
- public int ImageTypeLimit { get; set; }
- public bool EnableImages { get; set; }
- public bool AddProgramRecordingInfo { get; set; }
- public string DeviceId { get; set; }
- public bool EnableUserData { get; set; }
- public bool AddCurrentProgram { get; set; }
-
- public DtoOptions()
- : this(true)
- {
- }
-
- private static readonly ImageType[] AllImageTypes = Enum.GetNames(typeof(ImageType))
- .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
- .ToArray();
-
- private static readonly ItemFields[] AllItemFields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .Except(DefaultExcludedFields)
- .ToArray();
-
- public DtoOptions(bool allFields)
- {
- ImageTypeLimit = int.MaxValue;
- EnableImages = true;
- EnableUserData = true;
- AddCurrentProgram = true;
-
- if (allFields)
- {
- Fields = AllItemFields;
- }
- else
- {
- Fields = new ItemFields[] { };
- }
-
- ImageTypes = AllImageTypes;
- }
-
- public int GetImageLimit(ImageType type)
- {
- if (EnableImages && ImageTypes.Contains(type))
- {
- return ImageTypeLimit;
- }
-
- return 0;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
deleted file mode 100644
index 5ba6e036e..000000000
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Sync;
-
-namespace MediaBrowser.Controller.Dto
-{
- /// <summary>
- /// Interface IDtoService
- /// </summary>
- public interface IDtoService
- {
- /// <summary>
- /// Gets the dto id.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- string GetDtoId(BaseItem item);
-
- /// <summary>
- /// Attaches the primary image aspect ratio.
- /// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- void AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem 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(BaseItem item);
-
- /// <summary>
- /// Gets the base item dto.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="user">The user.</param>
- /// <param name="owner">The owner.</param>
- BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null);
-
- /// <summary>
- /// Gets the base item dto.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <param name="user">The user.</param>
- /// <param name="owner">The owner.</param>
- /// <returns>BaseItemDto.</returns>
- BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
-
- /// <summary>
- /// Gets the base item dtos.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="options">The options.</param>
- /// <param name="user">The user.</param>
- /// <param name="owner">The owner.</param>
- BaseItemDto[] GetBaseItemDtos(BaseItem[] items, DtoOptions options, User user = null, BaseItem owner = null);
-
- BaseItemDto[] GetBaseItemDtos(List<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
-
- /// <summary>
- /// Gets the chapter information dto.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>ChapterInfoDto.</returns>
- List<ChapterInfoDto> GetChapterInfoDtos(BaseItem item);
-
- /// <summary>
- /// Gets the user item data dto.
- /// </summary>
- /// <param name="data">The data.</param>
- /// <returns>UserItemDataDto.</returns>
- UserItemDataDto GetUserItemDataDto(UserItemData data);
-
- /// <summary>
- /// Gets the item by name dto.
- /// </summary>
- BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null);
-
- Dictionary<string, SyncedItemProgress> GetSyncedItemProgress(DtoOptions options);
- }
-}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
deleted file mode 100644
index 00fac1eab..000000000
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Specialized folder that can have items added to it's children by external entities.
- /// Used for our RootFolder so plug-ins can add items.
- /// </summary>
- public class AggregateFolder : Folder
- {
- public AggregateFolder()
- {
- PhysicalLocationsList = EmptyStringArray;
- }
-
- [IgnoreDataMember]
- public override bool IsPhysicalRoot
- {
- get { return true; }
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
- /// The _virtual children
- /// </summary>
- private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>();
-
- /// <summary>
- /// Gets the virtual children.
- /// </summary>
- /// <value>The virtual children.</value>
- public ConcurrentBag<BaseItem> VirtualChildren
- {
- get { return _virtualChildren; }
- }
-
- [IgnoreDataMember]
- public override string[] PhysicalLocations
- {
- get
- {
- return PhysicalLocationsList;
- }
- }
-
- public string[] PhysicalLocationsList { get; set; }
-
- protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
- {
- return CreateResolveArgs(directoryService, true).FileSystemChildren;
- }
-
- private Guid[] _childrenIds = null;
- private readonly object _childIdsLock = new object();
- protected override List<BaseItem> LoadChildren()
- {
- lock (_childIdsLock)
- {
- if (_childrenIds == null || _childrenIds.Length == 0)
- {
- var list = base.LoadChildren();
- _childrenIds = list.Select(i => i.Id).ToArray();
- return list;
- }
-
- return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
- }
- }
-
- private void ClearCache()
- {
- lock (_childIdsLock)
- {
- _childrenIds = null;
- }
- }
-
- private bool _requiresRefresh;
- public override bool RequiresRefresh()
- {
- var changed = base.RequiresRefresh() || _requiresRefresh;
-
- if (!changed)
- {
- var locations = PhysicalLocations;
-
- var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations;
-
- if (!locations.SequenceEqual(newLocations))
- {
- changed = true;
- }
- }
-
- return changed;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- ClearCache();
-
- var changed = base.BeforeMetadataRefresh() || _requiresRefresh;
- _requiresRefresh = false;
- return changed;
- }
-
- private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations)
- {
- ClearCache();
-
- var path = ContainingFolderPath;
-
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
- {
- FileInfo = FileSystem.GetDirectoryInfo(path),
- Path = path,
- Parent = GetParent() as Folder
- };
-
- // Gather child folder and files
- if (args.IsDirectory)
- {
- var isPhysicalRoot = args.IsPhysicalRoot;
-
- // When resolving the root, we need it's grandchildren (children of user views)
- var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
-
- var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
-
- // Need to remove subpaths that may have been resolved from shortcuts
- // Example: if \\server\movies exists, then strip out \\server\movies\action
- if (isPhysicalRoot)
- {
- files = LibraryManager.NormalizeRootPathList(files).ToArray();
- }
-
- args.FileSystemChildren = files;
- }
-
- _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations);
- if (setPhysicalLocations)
- {
- PhysicalLocationsList = args.PhysicalLocations;
- }
-
- return args;
- }
-
- protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
- }
-
- protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- ClearCache();
-
- await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)
- .ConfigureAwait(false);
-
- ClearCache();
- }
-
- /// <summary>
- /// Adds the virtual child.
- /// </summary>
- /// <param name="child">The child.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void AddVirtualChild(BaseItem child)
- {
- if (child == null)
- {
- throw new ArgumentNullException();
- }
-
- _virtualChildren.Add(child);
- }
-
- /// <summary>
- /// Finds the virtual child.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentNullException">id</exception>
- public BaseItem FindVirtualChild(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
-
- foreach (var child in _virtualChildren)
- {
- if (child.Id == id)
- {
- return child;
- }
- }
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
deleted file mode 100644
index 16fd75d2e..000000000
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ /dev/null
@@ -1,296 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-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
-{
- /// <summary>
- /// Class Audio
- /// </summary>
- public class Audio : BaseItem,
- IHasAlbumArtist,
- IHasArtist,
- IHasMusicGenres,
- IHasLookupInfo<SongInfo>,
- IHasMediaSources
- {
- /// <summary>
- /// Gets or sets the artist.
- /// </summary>
- /// <value>The artist.</value>
- [IgnoreDataMember]
- public string[] Artists { get; set; }
-
- [IgnoreDataMember]
- public string[] AlbumArtists { get; set; }
-
- [IgnoreDataMember]
- public override bool EnableRefreshOnDateModifiedChange
- {
- get { return true; }
- }
-
- public Audio()
- {
- Artists = EmptyStringArray;
- AlbumArtists = EmptyStringArray;
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return false; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- protected override bool SupportsOwnedItems
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override Folder LatestItemsIndexContainer
- {
- get
- {
- return AlbumEntity;
- }
- }
-
- public override bool CanDownload()
- {
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- [IgnoreDataMember]
- public string[] AllArtists
- {
- get
- {
- var list = new string[AlbumArtists.Length + Artists.Length];
-
- var index = 0;
- foreach (var artist in AlbumArtists)
- {
- list[index] = artist;
- index++;
- }
- foreach (var artist in Artists)
- {
- list[index] = artist;
- index++;
- }
-
- return list;
-
- }
- }
-
- [IgnoreDataMember]
- public MusicAlbum AlbumEntity
- {
- get { return FindParent<MusicAlbum>(); }
- }
-
- /// <summary>
- /// Gets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Audio;
- }
- }
-
- /// <summary>
- /// Creates the name of the sort.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateSortName()
- {
- return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "")
- + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
-
-
- if (ParentIndexNumber.HasValue)
- {
- songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;
- }
- songKey += Name;
-
- if (!string.IsNullOrWhiteSpace(Album))
- {
- songKey = Album + "-" + songKey;
- }
-
- var albumArtist = AlbumArtists.Length == 0 ? null : AlbumArtists[0];
- if (!string.IsNullOrWhiteSpace(albumArtist))
- {
- songKey = albumArtist + "-" + songKey;
- }
-
- list.Insert(0, songKey);
-
- return list;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- if (SourceType == SourceType.Library)
- {
- return UnratedItem.Music;
- }
- 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>();
-
- info.AlbumArtists = AlbumArtists;
- info.Album = Album;
- info.Artists = Artists;
-
- return info;
- }
-
- public virtual List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
- {
- if (SourceType == SourceType.Channel)
- {
- var sources = ChannelManager.GetStaticMediaSources(this, CancellationToken.None)
- .ToList();
-
- if (sources.Count > 0)
- {
- return sources;
- }
-
- var list = new List<MediaSourceInfo>
- {
- GetVersionInfo(this, enablePathSubstitution)
- };
-
- foreach (var mediaSource in list)
- {
- if (string.IsNullOrWhiteSpace(mediaSource.Path))
- {
- mediaSource.Type = MediaSourceType.Placeholder;
- }
- }
-
- return list;
- }
-
- var result = new List<MediaSourceInfo>
- {
- GetVersionInfo(this, enablePathSubstitution)
- };
-
- return result;
- }
-
- private static MediaSourceInfo GetVersionInfo(Audio i, bool enablePathSubstituion)
- {
- var locationType = i.LocationType;
-
- var info = new MediaSourceInfo
- {
- Id = i.Id.ToString("N"),
- Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
- MediaStreams = MediaSourceManager.GetMediaStreams(i.Id),
- Name = i.Name,
- Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
- RunTimeTicks = i.RunTimeTicks,
- Container = i.Container,
- Size = i.Size
- };
-
- if (info.Protocol == MediaProtocol.File)
- {
- info.ETag = i.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N");
- }
-
- if (string.IsNullOrEmpty(info.Container))
- {
- if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
- {
- info.Container = System.IO.Path.GetExtension(i.Path).TrimStart('.');
- }
- }
-
- info.Bitrate = i.TotalBitrate;
- info.InferTotalBitrate();
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs b/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
deleted file mode 100644
index 1b717b900..000000000
--- a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- public class AudioPodcast : Audio, IHasLookupInfo<SongInfo>
- {
- [IgnoreDataMember]
- public override bool SupportsPositionTicksResume
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return true;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
deleted file mode 100644
index b2dedada4..000000000
--- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- public interface IHasAlbumArtist
- {
- string[] AlbumArtists { get; set; }
- }
-
- public interface IHasArtist
- {
- string[] AllArtists { get; }
-
- string[] Artists { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs
deleted file mode 100644
index fdf939e35..000000000
--- a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- public interface IHasMusicGenres
- {
- List<string> Genres { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
deleted file mode 100644
index acda9ae02..000000000
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-using System;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- /// <summary>
- /// Class MusicAlbum
- /// </summary>
- public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
- {
- public string[] AlbumArtists { get; set; }
- public string[] Artists { get; set; }
-
- public MusicAlbum()
- {
- Artists = EmptyStringArray;
- AlbumArtists = EmptyStringArray;
- }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public MusicArtist MusicArtist
- {
- get { return GetMusicArtist(new DtoOptions(true)); }
- }
-
- public MusicArtist GetMusicArtist(DtoOptions options)
- {
- var parents = GetParents();
- foreach (var parent in parents)
- {
- var artist = parent as MusicArtist;
- if (artist != null)
- {
- return artist;
- }
- }
-
- var name = AlbumArtist;
- if (!string.IsNullOrWhiteSpace(name))
- {
- return LibraryManager.GetArtist(name, options);
- }
- return null;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsCumulativeRunTimeTicks
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public string[] AllArtists
- {
- get
- {
- var list = new string[AlbumArtists.Length + Artists.Length];
-
- var index = 0;
- foreach (var artist in AlbumArtists)
- {
- list[index] = artist;
- index++;
- }
- foreach (var artist in Artists)
- {
- list[index] = artist;
- index++;
- }
-
- return list;
- }
- }
-
- [IgnoreDataMember]
- public string AlbumArtist
- {
- get { return AlbumArtists.Length == 0 ? null : AlbumArtists[0]; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return false; }
- }
-
- /// <summary>
- /// Gets the tracks.
- /// </summary>
- /// <value>The tracks.</value>
- [IgnoreDataMember]
- public IEnumerable<BaseItem> Tracks
- {
- get
- {
- return GetRecursiveChildren(i => i is Audio);
- }
- }
-
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
- {
- return Tracks;
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var albumArtist = AlbumArtist;
- if (!string.IsNullOrWhiteSpace(albumArtist))
- {
- list.Insert(0, albumArtist + "-" + Name);
- }
-
- var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
-
- if (!string.IsNullOrWhiteSpace(id))
- {
- list.Insert(0, "MusicAlbum-Musicbrainz-" + id);
- }
-
- id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
-
- if (!string.IsNullOrWhiteSpace(id))
- {
- list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id);
- }
-
- return list;
- }
-
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- return config.BlockUnratedItems.Contains(UnratedItem.Music);
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Music;
- }
-
- public AlbumInfo GetLookupInfo()
- {
- var id = GetItemLookupInfo<AlbumInfo>();
-
- id.AlbumArtists = AlbumArtists;
-
- var artist = GetMusicArtist(new DtoOptions(false));
-
- if (artist != null)
- {
- id.ArtistProviderIds = artist.ProviderIds;
- }
-
- id.SongInfos = GetRecursiveChildren(i => i is Audio)
- .Cast<Audio>()
- .Select(i => i.GetLookupInfo())
- .ToList();
-
- var album = id.SongInfos
- .Select(i => i.Album)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
-
- if (!string.IsNullOrWhiteSpace(album))
- {
- id.Name = album;
- }
-
- return id;
- }
-
- public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var items = GetRecursiveChildren();
-
- var totalItems = items.Count;
- var numComplete = 0;
-
- var childUpdateType = ItemUpdateType.None;
-
- // Refresh songs
- foreach (var item in items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var updateType = await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- childUpdateType = childUpdateType | updateType;
-
- numComplete++;
- double percent = numComplete;
- percent /= totalItems;
- progress.Report(percent * 95);
- }
-
- var parentRefreshOptions = refreshOptions;
- if (childUpdateType > ItemUpdateType.None)
- {
- parentRefreshOptions = new MetadataRefreshOptions(refreshOptions);
- parentRefreshOptions.MetadataRefreshMode = MetadataRefreshMode.FullRefresh;
- }
-
- // Refresh current item
- await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
-
- if (!refreshOptions.IsAutomated)
- {
- await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
- {
- var all = AllArtists;
- foreach (var i in all)
- {
- // This should not be necessary but we're seeing some cases of it
- if (string.IsNullOrWhiteSpace(i))
- {
- continue;
- }
-
- var artist = LibraryManager.GetArtist(i);
-
- if (!artist.IsAccessedByName)
- {
- continue;
- }
-
- await artist.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
deleted file mode 100644
index 19fe68e25..000000000
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ /dev/null
@@ -1,345 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- /// <summary>
- /// Class MusicArtist
- /// </summary>
- public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo>
- {
- [IgnoreDataMember]
- public bool IsAccessedByName
- {
- get { return ParentId == Guid.Empty; }
- }
-
- [IgnoreDataMember]
- public override bool IsFolder
- {
- get
- {
- return !IsAccessedByName;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsCumulativeRunTimeTicks
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsDisplayedAsFolder
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- public override bool CanDelete()
- {
- return !IsAccessedByName;
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name };
- query.ArtistIds = new[] { Id.ToString("N") };
- }
-
- return LibraryManager.GetItemList(query);
- }
-
- [IgnoreDataMember]
- public override IEnumerable<BaseItem> Children
- {
- get
- {
- if (IsAccessedByName)
- {
- return new List<BaseItem>();
- }
-
- return base.Children;
- }
- }
-
- public override int GetChildCount(User user)
- {
- if (IsAccessedByName)
- {
- return 0;
- }
- return base.GetChildCount(user);
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- if (IsAccessedByName)
- {
- return true;
- }
-
- return base.IsSaveLocalMetadataEnabled();
- }
-
- private readonly Task _cachedTask = Task.FromResult(true);
- protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- if (IsAccessedByName)
- {
- // Should never get in here anyway
- return _cachedTask;
- }
-
- return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.InsertRange(0, GetUserDataKeys(this));
- return list;
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- private static List<string> GetUserDataKeys(MusicArtist item)
- {
- var list = new List<string>();
- var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
-
- if (!string.IsNullOrEmpty(id))
- {
- list.Add("Artist-Musicbrainz-" + id);
- }
-
- list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
- public override string CreatePresentationUniqueKey()
- {
- return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
- }
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- return config.BlockUnratedItems.Contains(UnratedItem.Music);
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Music;
- }
-
- public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var items = GetRecursiveChildren();
-
- var totalItems = items.Count;
- var numComplete = 0;
-
- var childUpdateType = ItemUpdateType.None;
-
- // Refresh songs
- foreach (var item in items)
- {
- if (!(item is Audio))
- {
- continue;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var updateType = await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- childUpdateType = childUpdateType | updateType;
-
- numComplete++;
- double percent = numComplete;
- percent /= totalItems;
- progress.Report(percent * 100);
- }
-
- var parentRefreshOptions = refreshOptions;
- if (childUpdateType > ItemUpdateType.None)
- {
- parentRefreshOptions = new MetadataRefreshOptions(refreshOptions);
- parentRefreshOptions.MetadataRefreshMode = MetadataRefreshMode.FullRefresh;
- }
-
- // Refresh current item
- await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
-
- // Refresh all non-songs
- foreach (var item in items)
- {
- if (item is Audio)
- {
- continue;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var updateType = await item.RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= totalItems;
- progress.Report(percent * 100);
- }
- }
-
- public ArtistInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<ArtistInfo>();
-
- info.SongInfos = GetRecursiveChildren(i => i is Audio)
- .Cast<Audio>()
- .Select(i => i.GetLookupInfo())
- .ToList();
-
- return info;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.ArtistsPath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- if (IsAccessedByName)
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (IsAccessedByName)
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
deleted file mode 100644
index 02e652048..000000000
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
- /// <summary>
- /// Class MusicGenre
- /// </summary>
- public class MusicGenre : BaseItem, IItemByName
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
- public override string CreatePresentationUniqueKey()
- {
- return GetUserDataKeys()[0];
- }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsDisplayedAsFolder
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- query.GenreIds = new[] { Id.ToString("N") };
- query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
-
- return LibraryManager.GetItemList(query);
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.MusicGenrePath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
deleted file mode 100644
index 374bb21f7..000000000
--- a/MediaBrowser.Controller/Entities/AudioBook.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class AudioBook : Audio.Audio, IHasSeries, IHasLookupInfo<SongInfo>
- {
- [IgnoreDataMember]
- public override bool SupportsPositionTicksResume
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public string SeriesPresentationUniqueKey { get; set; }
- [IgnoreDataMember]
- public string SeriesName { get; set; }
- [IgnoreDataMember]
- public Guid? SeriesId { get; set; }
-
- public string FindSeriesSortName()
- {
- return SeriesName;
- }
- public string FindSeriesName()
- {
- return SeriesName;
- }
- public string FindSeriesPresentationUniqueKey()
- {
- return SeriesPresentationUniqueKey;
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return null;
- }
-
- public Guid? FindSeriesId()
- {
- return SeriesId;
- }
-
- public override bool CanDownload()
- {
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Book;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
deleted file mode 100644
index 98899253e..000000000
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ /dev/null
@@ -1,2600 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Users;
-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.Controller.Dto;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class BaseItem
- /// </summary>
- 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 = EmptyGuidArray;
- ThemeVideoIds = EmptyGuidArray;
- Tags = EmptyStringArray;
- Genres = new List<string>();
- Studios = EmptyStringArray;
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- LockedFields = EmptyMetadataFieldsArray;
- ImageInfos = EmptyItemImageInfoArray;
- ProductionLocations = EmptyStringArray;
- }
-
- public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
- public static char SlugChar = '-';
-
- /// <summary>
- /// The supported image extensions
- /// </summary>
- public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" };
-
- public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
-
- /// <summary>
- /// The trailer folder name
- /// </summary>
- public static string TrailerFolderName = "trailers";
- public static string ThemeSongsFolderName = "theme-music";
- public static string ThemeSongFilename = "theme";
- public static string ThemeVideosFolderName = "backdrops";
-
- [IgnoreDataMember]
- public Guid[] ThemeSongIds { get; set; }
- [IgnoreDataMember]
- public Guid[] ThemeVideoIds { get; set; }
-
- [IgnoreDataMember]
- public string PreferredMetadataCountryCode { get; set; }
- [IgnoreDataMember]
- public string PreferredMetadataLanguage { get; set; }
-
- public long? Size { get; set; }
- public string Container { get; set; }
-
- [IgnoreDataMember]
- public string Tagline { get; set; }
-
- [IgnoreDataMember]
- public virtual ItemImageInfo[] ImageInfos { get; set; }
-
- [IgnoreDataMember]
- public bool IsVirtualItem { get; set; }
-
- /// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- [IgnoreDataMember]
- public string Album { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- [IgnoreDataMember]
- public string ChannelId { get; set; }
-
- [IgnoreDataMember]
- public virtual bool SupportsAddingToPlaylist
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool AlwaysScanInternalMetadataPath
- {
- get { return false; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is in mixed folder.
- /// </summary>
- /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsInMixedFolder { get; set; }
-
- [IgnoreDataMember]
- public virtual bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsPositionTicksResume
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsRemoteImageDownloading
- {
- get
- {
- return true;
- }
- }
-
- private string _name;
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- [IgnoreDataMember]
- public virtual string Name
- {
- get
- {
- return _name;
- }
- set
- {
- _name = value;
-
- // lazy load this again
- _sortName = null;
- }
- }
-
- [IgnoreDataMember]
- public bool IsUnaired
- {
- get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; }
- }
-
- [IgnoreDataMember]
- public int? TotalBitrate { get; set; }
- [IgnoreDataMember]
- public ExtraType? ExtraType { get; set; }
-
- [IgnoreDataMember]
- public bool IsThemeMedia
- {
- get
- {
- return ExtraType.HasValue && (ExtraType.Value == Model.Entities.ExtraType.ThemeSong || ExtraType.Value == Model.Entities.ExtraType.ThemeVideo);
- }
- }
-
- [IgnoreDataMember]
- public string OriginalTitle { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [IgnoreDataMember]
- public Guid Id { get; set; }
-
- [IgnoreDataMember]
- public Guid OwnerId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hd.
- /// </summary>
- /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool? IsHD { get; set; }
-
- /// <summary>
- /// Gets or sets the audio.
- /// </summary>
- /// <value>The audio.</value>
- [IgnoreDataMember]
- public ProgramAudio? Audio { get; set; }
-
- /// <summary>
- /// Return the id that should be used to key display prefs for this item.
- /// Default is based on the type for everything except actual generic folders.
- /// </summary>
- /// <value>The display prefs id.</value>
- [IgnoreDataMember]
- public virtual Guid DisplayPreferencesId
- {
- get
- {
- var thisType = GetType();
- return thisType == typeof(Folder) ? Id : thisType.FullName.GetMD5();
- }
- }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- [IgnoreDataMember]
- public virtual string Path { get; set; }
-
- [IgnoreDataMember]
- public virtual SourceType SourceType
- {
- get
- {
- if (!string.IsNullOrWhiteSpace(ChannelId))
- {
- return SourceType.Channel;
- }
-
- return SourceType.Library;
- }
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- [IgnoreDataMember]
- public virtual string ContainingFolderPath
- {
- get
- {
- if (IsFolder)
- {
- return Path;
- }
-
- return FileSystem.GetDirectoryName(Path);
- }
- }
-
- /// <summary>
- /// Gets or sets the name of the service.
- /// </summary>
- /// <value>The name of the service.</value>
- [IgnoreDataMember]
- public string ServiceName { get; set; }
-
- /// <summary>
- /// If this content came from an external service, the id of the content on that service
- /// </summary>
- [IgnoreDataMember]
- public string ExternalId { get; set; }
-
- [IgnoreDataMember]
- public string ExternalSeriesId { get; set; }
-
- /// <summary>
- /// Gets or sets the etag.
- /// </summary>
- /// <value>The etag.</value>
- [IgnoreDataMember]
- public string ExternalEtag { get; set; }
-
- [IgnoreDataMember]
- public virtual bool IsHidden
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool IsOwnedItem
- {
- get
- {
- if (OwnerId != Guid.Empty)
- {
- return true;
- }
-
- // legacy
-
- // Local trailer, special feature, theme video, etc.
- // An item that belongs to another item but is not part of the Parent-Child tree
- // This is a hack for now relying on ExtraType. Eventually we may need to persist this
- if (ParentId == Guid.Empty && !IsFolder && LocationType == LocationType.FileSystem)
- {
- return true;
- }
-
- return false;
- }
- }
-
- public BaseItem GetOwner()
- {
- var ownerId = OwnerId;
- return ownerId == Guid.Empty ? null : LibraryManager.GetItemById(ownerId);
- }
-
- /// <summary>
- /// Gets or sets the type of the location.
- /// </summary>
- /// <value>The type of the location.</value>
- [IgnoreDataMember]
- public virtual LocationType LocationType
- {
- get
- {
- //if (IsOffline)
- //{
- // return LocationType.Offline;
- //}
-
- if (string.IsNullOrWhiteSpace(Path))
- {
- if (SourceType == SourceType.Channel)
- {
- return LocationType.Remote;
- }
-
- return LocationType.Virtual;
- }
-
- return FileSystem.IsPathFile(Path) ? LocationType.FileSystem : LocationType.Remote;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsLocalMetadata
- {
- get
- {
- if (SourceType == SourceType.Channel)
- {
- return false;
- }
-
- var locationType = LocationType;
-
- return locationType != LocationType.Remote && locationType != LocationType.Virtual;
- }
- }
-
- [IgnoreDataMember]
- public virtual string FileNameWithoutExtension
- {
- get
- {
- if (LocationType == LocationType.FileSystem)
- {
- return System.IO.Path.GetFileNameWithoutExtension(Path);
- }
-
- return null;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool EnableAlphaNumericSorting
- {
- get
- {
- return true;
- }
- }
-
- private List<Tuple<StringBuilder, bool>> GetSortChunks(string s1)
- {
- var list = new List<Tuple<StringBuilder, bool>>();
-
- int thisMarker = 0, thisNumericChunk = 0;
-
- while (thisMarker < s1.Length)
- {
- if (thisMarker >= s1.Length)
- {
- break;
- }
- char thisCh = s1[thisMarker];
-
- StringBuilder thisChunk = new StringBuilder();
-
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0])))
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]);
- list.Add(new Tuple<StringBuilder, bool>(thisChunk, isNumeric));
- }
-
- return list;
- }
-
- /// <summary>
- /// This is just a helper for convenience
- /// </summary>
- /// <value>The primary image path.</value>
- [IgnoreDataMember]
- public string PrimaryImagePath
- {
- get { return this.GetImagePath(ImageType.Primary); }
- }
-
- public virtual bool IsInternetMetadataEnabled()
- {
- return LibraryManager.GetLibraryOptions(this).EnableInternetProviders;
- }
-
- public virtual bool CanDelete()
- {
- if (SourceType == SourceType.Channel)
- {
- return false;
- }
-
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- public virtual bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
- {
- if (user.Policy.EnableContentDeletion)
- {
- return true;
- }
-
- var allowed = user.Policy.EnableContentDeletionFromFolders;
- var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
-
- foreach (var folder in collectionFolders)
- {
- if (allowed.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- return false;
- }
-
- public bool CanDelete(User user, List<Folder> allCollectionFolders)
- {
- return CanDelete() && IsAuthorizedToDelete(user, allCollectionFolders);
- }
-
- public bool CanDelete(User user)
- {
- var allCollectionFolders = LibraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
- return CanDelete(user, allCollectionFolders);
- }
-
- public virtual bool CanDownload()
- {
- return false;
- }
-
- public virtual bool IsAuthorizedToDownload(User user)
- {
- return user.Policy.EnableContentDownloading;
- }
-
- public bool CanDownload(User user)
- {
- return CanDownload() && IsAuthorizedToDownload(user);
- }
-
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- [IgnoreDataMember]
- public DateTime DateCreated { get; set; }
-
- /// <summary>
- /// Gets or sets the date modified.
- /// </summary>
- /// <value>The date modified.</value>
- [IgnoreDataMember]
- public DateTime DateModified { get; set; }
-
- [IgnoreDataMember]
- public DateTime DateLastSaved { get; set; }
-
- [IgnoreDataMember]
- public DateTime DateLastRefreshed { get; set; }
-
- [IgnoreDataMember]
- public virtual bool EnableRefreshOnDateModifiedChange
- {
- get { return false; }
- }
-
- /// <summary>
- /// The logger
- /// </summary>
- public static ILogger Logger { get; set; }
- public static ILibraryManager LibraryManager { get; set; }
- public static IServerConfigurationManager ConfigurationManager { get; set; }
- public static IProviderManager ProviderManager { get; set; }
- public static ILocalizationManager LocalizationManager { get; set; }
- public static IItemRepository ItemRepository { get; set; }
- public static IFileSystem FileSystem { get; set; }
- public static IUserDataManager UserDataManager { get; set; }
- public static ILiveTvManager LiveTvManager { get; set; }
- public static IChannelManager ChannelManager { get; set; }
- public static ICollectionManager CollectionManager { get; set; }
- public static IImageProcessor ImageProcessor { get; set; }
- public static IMediaSourceManager MediaSourceManager { get; set; }
- public static IMediaEncoder MediaEncoder { get; set; }
-
- /// <summary>
- /// Returns a <see cref="System.String" /> that represents this instance.
- /// </summary>
- /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
- public override string ToString()
- {
- return Name;
- }
-
- [IgnoreDataMember]
- public bool IsLocked { get; set; }
-
- /// <summary>
- /// Gets or sets the locked fields.
- /// </summary>
- /// <value>The locked fields.</value>
- [IgnoreDataMember]
- public MetadataFields[] LockedFields { get; set; }
-
- /// <summary>
- /// Gets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- [IgnoreDataMember]
- public virtual string MediaType
- {
- get
- {
- return null;
- }
- }
-
- [IgnoreDataMember]
- public virtual string[] PhysicalLocations
- {
- get
- {
- var locationType = LocationType;
-
- if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
- {
- return new string[] { };
- }
-
- return new[] { Path };
- }
- }
-
- private string _forcedSortName;
- /// <summary>
- /// Gets or sets the name of the forced sort.
- /// </summary>
- /// <value>The name of the forced sort.</value>
- [IgnoreDataMember]
- public string ForcedSortName
- {
- get { return _forcedSortName; }
- set { _forcedSortName = value; _sortName = null; }
- }
-
- private string _sortName;
- /// <summary>
- /// Gets the name of the sort.
- /// </summary>
- /// <value>The name of the sort.</value>
- [IgnoreDataMember]
- public string SortName
- {
- get
- {
- if (_sortName == null)
- {
- if (!string.IsNullOrWhiteSpace(ForcedSortName))
- {
- // Need the ToLower because that's what CreateSortName does
- _sortName = ModifySortChunks(ForcedSortName).ToLower();
- }
- else
- {
- _sortName = CreateSortName();
- }
- }
- return _sortName;
- }
- set
- {
- _sortName = value;
- }
- }
-
- public string GetInternalMetadataPath()
- {
- var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
-
- return GetInternalMetadataPath(basePath);
- }
-
- protected virtual string GetInternalMetadataPath(string basePath)
- {
- if (SourceType == SourceType.Channel)
- {
- return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
- }
-
- var idString = Id.ToString("N");
-
- basePath = System.IO.Path.Combine(basePath, "library");
-
- return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
- }
-
- /// <summary>
- /// Creates the name of the sort.
- /// </summary>
- /// <returns>System.String.</returns>
- protected virtual string CreateSortName()
- {
- if (Name == null) return null; //some items may not have name filled in properly
-
- if (!EnableAlphaNumericSorting)
- {
- return Name.TrimStart();
- }
-
- var sortable = Name.Trim().ToLower();
-
- foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters)
- {
- sortable = sortable.Replace(removeChar, string.Empty);
- }
-
- foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters)
- {
- sortable = sortable.Replace(replaceChar, " ");
- }
-
- foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
- {
- // Remove from beginning if a space follows
- if (sortable.StartsWith(search + " "))
- {
- sortable = sortable.Remove(0, search.Length + 1);
- }
- // Remove from middle if surrounded by spaces
- sortable = sortable.Replace(" " + search + " ", " ");
-
- // Remove from end if followed by a space
- if (sortable.EndsWith(" " + search))
- {
- sortable = sortable.Remove(sortable.Length - (search.Length + 1));
- }
- }
-
- return ModifySortChunks(sortable);
- }
-
- private string ModifySortChunks(string name)
- {
- var chunks = GetSortChunks(name);
-
- var builder = new StringBuilder();
-
- foreach (var chunk in chunks)
- {
- var chunkBuilder = chunk.Item1;
-
- // This chunk is numeric
- if (chunk.Item2)
- {
- while (chunkBuilder.Length < 10)
- {
- chunkBuilder.Insert(0, '0');
- }
- }
-
- builder.Append(chunkBuilder);
- }
- //Logger.Debug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
- return builder.ToString().RemoveDiacritics();
- }
-
- [IgnoreDataMember]
- public Guid ParentId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent.
- /// </summary>
- /// <value>The parent.</value>
- [IgnoreDataMember]
- public Folder Parent
- {
- get { return GetParent() as Folder; }
- set
- {
-
- }
- }
-
- public void SetParent(Folder parent)
- {
- ParentId = parent == null ? Guid.Empty : parent.Id;
- }
-
- public BaseItem GetParent()
- {
- var parentId = ParentId;
- if (parentId != Guid.Empty)
- {
- return LibraryManager.GetItemById(parentId);
- }
-
- return null;
- }
-
- public IEnumerable<BaseItem> GetParents()
- {
- var parent = GetParent();
-
- while (parent != null)
- {
- yield return parent;
-
- parent = parent.GetParent();
- }
- }
-
- /// <summary>
- /// Finds a parent of a given type
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T FindParent<T>()
- where T : Folder
- {
- foreach (var parent in GetParents())
- {
- var item = parent as T;
- if (item != null)
- {
- return item;
- }
- }
- return null;
- }
-
- [IgnoreDataMember]
- public virtual Guid? DisplayParentId
- {
- get
- {
- var parentId = ParentId;
-
- if (parentId == Guid.Empty)
- {
- return null;
- }
- return parentId;
- }
- }
-
- [IgnoreDataMember]
- public BaseItem DisplayParent
- {
- get
- {
- var id = DisplayParentId;
- if (!id.HasValue || id.Value == Guid.Empty)
- {
- return null;
- }
- return LibraryManager.GetItemById(id.Value);
- }
- }
-
- /// <summary>
- /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
- /// </summary>
- /// <value>The premiere date.</value>
- [IgnoreDataMember]
- public DateTime? PremiereDate { get; set; }
-
- /// <summary>
- /// Gets or sets the end date.
- /// </summary>
- /// <value>The end date.</value>
- [IgnoreDataMember]
- public DateTime? EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- [IgnoreDataMember]
- public string OfficialRating { get; set; }
-
- [IgnoreDataMember]
- public int InheritedParentalRatingValue { get; set; }
-
- /// <summary>
- /// Gets or sets the critic rating.
- /// </summary>
- /// <value>The critic rating.</value>
- [IgnoreDataMember]
- public float? CriticRating { get; set; }
-
- /// <summary>
- /// Gets or sets the custom rating.
- /// </summary>
- /// <value>The custom rating.</value>
- [IgnoreDataMember]
- public string CustomRating { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- [IgnoreDataMember]
- public string Overview { get; set; }
-
- /// <summary>
- /// Gets or sets the studios.
- /// </summary>
- /// <value>The studios.</value>
- [IgnoreDataMember]
- public string[] Studios { get; set; }
-
- /// <summary>
- /// Gets or sets the genres.
- /// </summary>
- /// <value>The genres.</value>
- [IgnoreDataMember]
- public List<string> Genres { get; set; }
-
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- [IgnoreDataMember]
- public string[] Tags { get; set; }
-
- [IgnoreDataMember]
- public string[] ProductionLocations { get; set; }
-
- /// <summary>
- /// Gets or sets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- [IgnoreDataMember]
- public string HomePageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- [IgnoreDataMember]
- public float? CommunityRating { get; set; }
-
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- [IgnoreDataMember]
- public long? RunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the production year.
- /// </summary>
- /// <value>The production year.</value>
- [IgnoreDataMember]
- public int? ProductionYear { get; set; }
-
- /// <summary>
- /// If the item is part of a series, this is it's number in the series.
- /// This could be episode number, album track number, etc.
- /// </summary>
- /// <value>The index number.</value>
- [IgnoreDataMember]
- public int? IndexNumber { get; set; }
-
- /// <summary>
- /// For an episode this could be the season number, or for a song this could be the disc number.
- /// </summary>
- /// <value>The parent index number.</value>
- [IgnoreDataMember]
- public int? ParentIndexNumber { get; set; }
-
- [IgnoreDataMember]
- public string OfficialRatingForComparison
- {
- get
- {
- var officialRating = OfficialRating;
- if (!string.IsNullOrWhiteSpace(officialRating))
- {
- return officialRating;
- }
-
- var parent = DisplayParent;
- if (parent != null)
- {
- return parent.OfficialRatingForComparison;
- }
-
- return null;
- }
- }
-
- [IgnoreDataMember]
- public string CustomRatingForComparison
- {
- get
- {
- var customRating = CustomRating;
- if (!string.IsNullOrWhiteSpace(customRating))
- {
- return customRating;
- }
-
- var parent = DisplayParent;
- if (parent != null)
- {
- return parent.CustomRatingForComparison;
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Gets the play access.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>PlayAccess.</returns>
- public PlayAccess GetPlayAccess(User user)
- {
- if (!user.Policy.EnableMediaPlayback)
- {
- return PlayAccess.None;
- }
-
- //if (!user.IsParentalScheduleAllowed())
- //{
- // return PlayAccess.None;
- //}
-
- return PlayAccess.Full;
- }
-
- /// <summary>
- /// Loads the theme songs.
- /// </summary>
- /// <returns>List{Audio.Audio}.</returns>
- 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 => FileSystem.GetFiles(i.FullName))
- .ToList();
-
- // Support plex/xbmc convention
- files.AddRange(fileSystemChildren
- .Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
- );
-
- return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
- .OfType<Audio.Audio>()
- .Select(audio =>
- {
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
-
- if (dbItem != null)
- {
- audio = dbItem;
- }
- else
- {
- // item is new
- audio.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeSong;
- }
-
- return audio;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToArray();
- }
-
- /// <summary>
- /// Loads the video backdrops.
- /// </summary>
- /// <returns>List{Video}.</returns>
- 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 => FileSystem.GetFiles(i.FullName));
-
- return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
- .OfType<Video>()
- .Select(item =>
- {
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = LibraryManager.GetItemById(item.Id) as Video;
-
- if (dbItem != null)
- {
- item = dbItem;
- }
- else
- {
- // item is new
- item.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeVideo;
- }
-
- return item;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToArray();
- }
-
- public Task RefreshMetadata(CancellationToken cancellationToken)
- {
- return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken);
- }
-
- protected virtual void TriggerOnRefreshStart()
- {
-
- }
-
- protected virtual void TriggerOnRefreshComplete()
- {
-
- }
-
- /// <summary>
- /// Overrides the base implementation to refresh metadata for local trailers
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>true if a provider reports we changed</returns>
- public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- TriggerOnRefreshStart();
-
- var locationType = LocationType;
-
- var requiresSave = false;
-
- if (SupportsOwnedItems)
- {
- try
- {
- var files = locationType != LocationType.Remote && locationType != LocationType.Virtual ?
- GetFileSystemChildren(options.DirectoryService).ToList() :
- new List<FileSystemMetadata>();
-
- var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
-
- if (ownedItemsChanged)
- {
- requiresSave = true;
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error refreshing owned items for {0}", ex, Path ?? Name);
- }
- }
-
- try
- {
- var refreshOptions = requiresSave
- ? new MetadataRefreshOptions(options)
- {
- ForceSave = true
- }
- : options;
-
- return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
- }
- finally
- {
- TriggerOnRefreshComplete();
- }
- }
-
- [IgnoreDataMember]
- protected virtual bool SupportsOwnedItems
- {
- get { return IsFolder || GetParent() != null; }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsPeople
- {
- get { return false; }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsThemeMedia
- {
- get { return false; }
- }
-
- /// <summary>
- /// Refreshes owned items such as trailers, theme videos, special features, etc.
- /// Returns true or false indicating if changes were found.
- /// </summary>
- /// <param name="options"></param>
- /// <param name="fileSystemChildren"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- protected virtual async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var themeSongsChanged = false;
-
- var themeVideosChanged = false;
-
- var localTrailersChanged = false;
-
- if (LocationType == LocationType.FileSystem && GetParent() != null)
- {
- if (SupportsThemeMedia)
- {
- if (!IsInMixedFolder)
- {
- themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
-
- themeVideosChanged = await RefreshThemeVideos(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
- }
- }
-
- var hasTrailers = this as IHasTrailers;
- if (hasTrailers != null)
- {
- localTrailersChanged = await RefreshLocalTrailers(hasTrailers, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
- }
- }
-
- return themeSongsChanged || themeVideosChanged || localTrailersChanged;
- }
-
- protected virtual FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
- {
- var path = ContainingFolderPath;
-
- return directoryService.GetFileSystemEntries(path);
- }
-
- private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
-
- var newItemIds = newItems.Select(i => i.Id).ToArray();
-
- var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
- var ownerId = item.Id;
-
- var tasks = newItems.Select(i =>
- {
- var subOptions = new MetadataRefreshOptions(options);
-
- if (!i.ExtraType.HasValue ||
- i.ExtraType.Value != Model.Entities.ExtraType.Trailer ||
- i.OwnerId != ownerId ||
- i.ParentId != Guid.Empty)
- {
- i.ExtraType = Model.Entities.ExtraType.Trailer;
- i.OwnerId = ownerId;
- i.ParentId = Guid.Empty;
- subOptions.ForceSave = true;
- }
-
- return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
- });
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
- item.LocalTrailerIds = newItemIds;
-
- return itemsChanged;
- }
-
- private async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService);
-
- var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToArray(newThemeVideos.Length);
-
- var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
-
- var ownerId = item.Id;
-
- var tasks = newThemeVideos.Select(i =>
- {
- var subOptions = new MetadataRefreshOptions(options);
-
- if (!i.ExtraType.HasValue ||
- i.ExtraType.Value != Model.Entities.ExtraType.ThemeVideo ||
- i.OwnerId != ownerId ||
- i.ParentId != Guid.Empty)
- {
- i.ExtraType = Model.Entities.ExtraType.ThemeVideo;
- i.OwnerId = ownerId;
- i.ParentId = Guid.Empty;
- subOptions.ForceSave = true;
- }
-
- return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
- });
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
- item.ThemeVideoIds = newThemeVideoIds;
-
- return themeVideosChanged;
- }
-
- /// <summary>
- /// Refreshes the theme songs.
- /// </summary>
- private async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService);
- var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToArray(newThemeSongs.Length);
-
- var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
-
- var ownerId = item.Id;
-
- var tasks = newThemeSongs.Select(i =>
- {
- var subOptions = new MetadataRefreshOptions(options);
-
- if (!i.ExtraType.HasValue ||
- i.ExtraType.Value != Model.Entities.ExtraType.ThemeSong ||
- i.OwnerId != ownerId ||
- i.ParentId != Guid.Empty)
- {
- i.ExtraType = Model.Entities.ExtraType.ThemeSong;
- i.OwnerId = ownerId;
- i.ParentId = Guid.Empty;
- subOptions.ForceSave = true;
- }
-
- return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
- });
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
- item.ThemeSongIds = newThemeSongIds;
-
- return themeSongsChanged;
- }
-
- /// <summary>
- /// Gets or sets the provider ids.
- /// </summary>
- /// <value>The provider ids.</value>
- [IgnoreDataMember]
- public Dictionary<string, string> ProviderIds { get; set; }
-
- [IgnoreDataMember]
- public virtual Folder LatestItemsIndexContainer
- {
- get { return null; }
- }
-
- public virtual double? GetDefaultPrimaryImageAspectRatio()
- {
- return null;
- }
-
- public virtual string CreatePresentationUniqueKey()
- {
- return Id.ToString("N");
- }
-
- [IgnoreDataMember]
- public string PresentationUniqueKey { get; set; }
-
- public string GetPresentationUniqueKey()
- {
- return PresentationUniqueKey ?? CreatePresentationUniqueKey();
- }
-
- public virtual bool RequiresRefresh()
- {
- return false;
- }
-
- public virtual List<string> GetUserDataKeys()
- {
- var list = new List<string>();
-
- if (SourceType == SourceType.Channel)
- {
- if (!string.IsNullOrWhiteSpace(ExternalId))
- {
- list.Add(ExternalId);
- }
- }
-
- list.Add(Id.ToString());
- return list;
- }
-
- internal virtual ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
- {
- var updateType = ItemUpdateType.None;
-
- if (IsInMixedFolder != newItem.IsInMixedFolder)
- {
- IsInMixedFolder = newItem.IsInMixedFolder;
- updateType |= ItemUpdateType.MetadataImport;
- }
-
- return updateType;
- }
-
- public void AfterMetadataRefresh()
- {
- _sortName = null;
- }
-
- /// <summary>
- /// Gets the preferred metadata language.
- /// </summary>
- /// <returns>System.String.</returns>
- public string GetPreferredMetadataLanguage()
- {
- string lang = PreferredMetadataLanguage;
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = GetParents()
- .Select(i => i.PreferredMetadataLanguage)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = LibraryManager.GetCollectionFolders(this)
- .Select(i => i.PreferredMetadataLanguage)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = LibraryManager.GetLibraryOptions(this).PreferredMetadataLanguage;
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = ConfigurationManager.Configuration.PreferredMetadataLanguage;
- }
-
- return lang;
- }
-
- /// <summary>
- /// Gets the preferred metadata language.
- /// </summary>
- /// <returns>System.String.</returns>
- public string GetPreferredMetadataCountryCode()
- {
- string lang = PreferredMetadataCountryCode;
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = GetParents()
- .Select(i => i.PreferredMetadataCountryCode)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = LibraryManager.GetCollectionFolders(this)
- .Select(i => i.PreferredMetadataCountryCode)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = LibraryManager.GetLibraryOptions(this).MetadataCountryCode;
- }
-
- if (string.IsNullOrWhiteSpace(lang))
- {
- lang = ConfigurationManager.Configuration.MetadataCountryCode;
- }
-
- return lang;
- }
-
- public virtual bool IsSaveLocalMetadataEnabled()
- {
- if (SourceType == SourceType.Channel)
- {
- return false;
- }
-
- var libraryOptions = LibraryManager.GetLibraryOptions(this);
-
- return libraryOptions.SaveLocalMetadata;
- }
-
- /// <summary>
- /// Determines if a given user has access to this item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns><c>true</c> if [is parental allowed] [the specified user]; otherwise, <c>false</c>.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public bool IsParentalAllowed(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- if (!IsVisibleViaTags(user))
- {
- return false;
- }
-
- var maxAllowedRating = user.Policy.MaxParentalRating;
-
- if (maxAllowedRating == null)
- {
- return true;
- }
-
- var rating = CustomRatingForComparison;
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- rating = OfficialRatingForComparison;
- }
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- return !GetBlockUnratedValue(user.Policy);
- }
-
- var value = LocalizationManager.GetRatingLevel(rating);
-
- // Could not determine the integer value
- if (!value.HasValue)
- {
- var isAllowed = !GetBlockUnratedValue(user.Policy);
-
- if (!isAllowed)
- {
- Logger.Debug("{0} has an unrecognized parental rating of {1}.", Name, rating);
- }
-
- return isAllowed;
- }
-
- return value.Value <= maxAllowedRating.Value;
- }
-
- public int? GetParentalRatingValue()
- {
- var rating = CustomRating;
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- rating = OfficialRating;
- }
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- return null;
- }
-
- return LocalizationManager.GetRatingLevel(rating);
- }
-
- public int? GetInheritedParentalRatingValue()
- {
- var rating = CustomRatingForComparison;
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- rating = OfficialRatingForComparison;
- }
-
- if (string.IsNullOrWhiteSpace(rating))
- {
- return null;
- }
-
- return LocalizationManager.GetRatingLevel(rating);
- }
-
- public List<string> GetInheritedTags()
- {
- var list = new List<string>();
- list.AddRange(Tags);
-
- foreach (var parent in GetParents())
- {
- list.AddRange(parent.Tags);
- }
-
- return list.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
- }
-
- private bool IsVisibleViaTags(User user)
- {
- var policy = user.Policy;
- if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
-
- return true;
- }
-
- protected virtual bool IsAllowTagFilterEnforced()
- {
- return true;
- }
-
- public virtual UnratedItem GetBlockUnratedType()
- {
- if (SourceType == SourceType.Channel)
- {
- return UnratedItem.ChannelContent;
- }
-
- return UnratedItem.Other;
- }
-
- /// <summary>
- /// Gets the block unrated value.
- /// </summary>
- /// <param name="config">The configuration.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- protected virtual bool GetBlockUnratedValue(UserPolicy config)
- {
- // Don't block plain folders that are unrated. Let the media underneath get blocked
- // Special folders like series and albums will override this method.
- if (IsFolder)
- {
- return false;
- }
- if (this is IItemByName)
- {
- return false;
- }
-
- return config.BlockUnratedItems.Contains(GetBlockUnratedType());
- }
-
- /// <summary>
- /// Determines if this folder should be visible to a given user.
- /// Default is just parental allowed. Can be overridden for more functionality.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns><c>true</c> if the specified user is visible; otherwise, <c>false</c>.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public virtual bool IsVisible(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- return IsParentalAllowed(user);
- }
-
- public virtual bool IsVisibleStandalone(User user)
- {
- if (SourceType == SourceType.Channel)
- {
- return IsVisibleStandaloneInternal(user, false) && Channel.IsChannelVisible(this, user);
- }
-
- return IsVisibleStandaloneInternal(user, true);
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsInheritedParentImages
- {
- get { return false; }
- }
-
- protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
- {
- if (!IsVisible(user))
- {
- return false;
- }
-
- if (GetParents().Any(i => !i.IsVisible(user)))
- {
- return false;
- }
-
- if (checkFolders)
- {
- var topParent = GetParents().LastOrDefault() ?? this;
-
- if (string.IsNullOrWhiteSpace(topParent.Path))
- {
- return true;
- }
-
- var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id).ToList();
-
- if (itemCollectionFolders.Count > 0)
- {
- var userCollectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList();
- if (!itemCollectionFolders.Any(userCollectionFolders.Contains))
- {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is folder.
- /// </summary>
- /// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public virtual bool IsFolder
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool IsDisplayedAsFolder
- {
- get
- {
- return false;
- }
- }
-
- public virtual string GetClientTypeName()
- {
- if (IsFolder && SourceType == SourceType.Channel && !(this is Channel))
- {
- return "ChannelFolderItem";
- }
-
- return GetType().Name;
- }
-
- /// <summary>
- /// Gets the linked child.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>BaseItem.</returns>
- protected BaseItem GetLinkedChild(LinkedChild info)
- {
- // First get using the cached Id
- if (info.ItemId.HasValue)
- {
- if (info.ItemId.Value == Guid.Empty)
- {
- return null;
- }
-
- var itemById = LibraryManager.GetItemById(info.ItemId.Value);
-
- if (itemById != null)
- {
- return itemById;
- }
- }
-
- var item = FindLinkedChild(info);
-
- // If still null, log
- if (item == null)
- {
- // Don't keep searching over and over
- info.ItemId = Guid.Empty;
- }
- else
- {
- // Cache the id for next time
- info.ItemId = item.Id;
- }
-
- return item;
- }
-
- private BaseItem FindLinkedChild(LinkedChild info)
- {
- if (!string.IsNullOrWhiteSpace(info.Path))
- {
- var itemByPath = LibraryManager.FindByPath(info.Path, null);
-
- if (itemByPath == null)
- {
- //Logger.Warn("Unable to find linked item at path {0}", info.Path);
- }
-
- return itemByPath;
- }
-
- return null;
- }
-
- [IgnoreDataMember]
- public virtual bool EnableRememberingTrackSelections
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
- /// Adds a studio to the item
- /// </summary>
- /// <param name="name">The name.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void AddStudio(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- var current = Studios;
-
- if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- 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>
- /// <param name="name">The name.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void AddGenre(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- if (!Genres.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- Genres.Add(name);
- }
- }
-
- /// <summary>
- /// Marks the played.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="datePlayed">The date played.</param>
- /// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public virtual void MarkPlayed(User user,
- DateTime? datePlayed,
- bool resetPosition)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
-
- var data = UserDataManager.GetUserData(user, this);
-
- if (datePlayed.HasValue)
- {
- // Increment
- data.PlayCount++;
- }
-
- // Ensure it's at least one
- data.PlayCount = Math.Max(data.PlayCount, 1);
-
- if (resetPosition)
- {
- data.PlaybackPositionTicks = 0;
- }
-
- data.LastPlayedDate = datePlayed ?? data.LastPlayedDate ?? DateTime.UtcNow;
- data.Played = true;
-
- UserDataManager.SaveUserData(user.Id, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
- }
-
- /// <summary>
- /// Marks the unplayed.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public virtual void MarkUnplayed(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
-
- var data = UserDataManager.GetUserData(user, this);
-
- //I think it is okay to do this here.
- // if this is only called when a user is manually forcing something to un-played
- // then it probably is what we want to do...
- data.PlayCount = 0;
- data.PlaybackPositionTicks = 0;
- data.LastPlayedDate = null;
- data.Played = false;
-
- UserDataManager.SaveUserData(user.Id, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
- }
-
- /// <summary>
- /// Do whatever refreshing is necessary when the filesystem pertaining to this item has changed.
- /// </summary>
- /// <returns>Task.</returns>
- public virtual void ChangedExternally()
- {
- ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem)
- {
- ValidateChildren = true,
-
- }, RefreshPriority.High);
- }
-
- /// <summary>
- /// Gets an 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>
- /// <exception cref="System.ArgumentException">Backdrops should be accessed using Item.Backdrops</exception>
- public bool HasImage(ImageType type, int imageIndex)
- {
- return GetImageInfo(type, imageIndex) != null;
- }
-
- public void SetImage(ItemImageInfo image, int index)
- {
- if (image.Type == ImageType.Chapter)
- {
- throw new ArgumentException("Cannot set chapter images using SetImagePath");
- }
-
- var existingImage = GetImageInfo(image.Type, index);
-
- if (existingImage != null)
- {
- existingImage.Path = image.Path;
- existingImage.DateModified = image.DateModified;
- existingImage.Width = image.Width;
- existingImage.Height = image.Height;
- }
-
- 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)
- {
- if (type == ImageType.Chapter)
- {
- throw new ArgumentException("Cannot set chapter images using SetImagePath");
- }
-
- var image = GetImageInfo(type, index);
-
- if (image == null)
- {
- var currentCount = ImageInfos.Length;
- var newList = ImageInfos.ToArray(currentCount + 1);
- newList[currentCount] = GetImageInfo(file, type);
- ImageInfos = newList;
- }
- else
- {
- var imageInfo = GetImageInfo(file, type);
-
- image.Path = file.FullName;
- image.DateModified = imageInfo.DateModified;
-
- // reset these values
- image.Width = 0;
- image.Height = 0;
- }
- }
-
- /// <summary>
- /// Deletes the image.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="index">The index.</param>
- /// <returns>Task.</returns>
- public void DeleteImage(ImageType type, int index)
- {
- var info = GetImageInfo(type, index);
-
- if (info == null)
- {
- // Nothing to do
- return;
- }
-
- // Remove it from the item
- RemoveImage(info);
-
- if (info.IsLocalFile)
- {
- FileSystem.DeleteFile(info.Path);
- }
-
- UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
- }
-
- public void RemoveImage(ItemImageInfo image)
- {
- RemoveImages(new List<ItemImageInfo> { image });
- }
-
- public void RemoveImages(List<ItemImageInfo> deletedImages)
- {
- ImageInfos = ImageInfos.Except(deletedImages).ToArray();
- }
-
- public virtual void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- LibraryManager.UpdateItem(this, updateReason, cancellationToken);
- }
-
- /// <summary>
- /// Validates that images within the item are still on the file system
- /// </summary>
- public bool ValidateImages(IDirectoryService directoryService)
- {
- var allFiles = ImageInfos
- .Where(i => i.IsLocalFile)
- .Select(i => FileSystem.GetDirectoryName(i.Path))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .SelectMany(i => directoryService.GetFilePaths(i))
- .ToList();
-
- var deletedImages = ImageInfos
- .Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
- .ToList();
-
- if (deletedImages.Count > 0)
- {
- ImageInfos = ImageInfos.Except(deletedImages).ToArray();
- }
-
- return deletedImages.Count > 0;
- }
-
- /// <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>
- /// <exception cref="System.InvalidOperationException">
- /// </exception>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public string GetImagePath(ImageType imageType, int imageIndex)
- {
- var info = GetImageInfo(imageType, imageIndex);
-
- return info == null ? null : info.Path;
- }
-
- /// <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>
- public ItemImageInfo GetImageInfo(ImageType imageType, int imageIndex)
- {
- if (imageType == ImageType.Chapter)
- {
- var chapter = ItemRepository.GetChapter(Id, imageIndex);
-
- if (chapter == null)
- {
- return null;
- }
-
- var path = chapter.ImagePath;
-
- if (string.IsNullOrWhiteSpace(path))
- {
- return null;
- }
-
- return new ItemImageInfo
- {
- Path = path,
- DateModified = chapter.ImageDateModified,
- Type = imageType
- };
- }
-
- return GetImages(imageType)
- .ElementAtOrDefault(imageIndex);
- }
-
- public IEnumerable<ItemImageInfo> GetImages(ImageType imageType)
- {
- if (imageType == ImageType.Chapter)
- {
- throw new ArgumentException("No image info for chapter images");
- }
-
- return ImageInfos.Where(i => i.Type == imageType);
- }
-
- /// <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>
- /// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
- public bool AddImages(ImageType imageType, List<FileSystemMetadata> images)
- {
- if (imageType == ImageType.Chapter)
- {
- throw new ArgumentException("Cannot call AddImages with chapter images");
- }
-
- var existingImages = GetImages(imageType)
- .ToList();
-
- var newImageList = new List<FileSystemMetadata>();
- var imageAdded = false;
- var imageUpdated = false;
-
- foreach (var newImage in images)
- {
- if (newImage == null)
- {
- throw new ArgumentException("null image found in list");
- }
-
- var existing = existingImages
- .FirstOrDefault(i => string.Equals(i.Path, newImage.FullName, StringComparison.OrdinalIgnoreCase));
-
- if (existing == null)
- {
- newImageList.Add(newImage);
- imageAdded = true;
- }
- else
- {
- if (existing.IsLocalFile)
- {
- var newDateModified = FileSystem.GetLastWriteTimeUtc(newImage);
-
- // If date changed then we need to reset saved image dimensions
- if (existing.DateModified != newDateModified && (existing.Width > 0 || existing.Height > 0))
- {
- existing.Width = 0;
- existing.Height = 0;
- imageUpdated = true;
- }
-
- existing.DateModified = newDateModified;
- }
- }
- }
-
- if (imageAdded || images.Count != existingImages.Count)
- {
- var newImagePaths = images.Select(i => i.FullName).ToList();
-
- var deleted = existingImages
- .Where(i => i.IsLocalFile && !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path))
- .ToList();
-
- if (deleted.Count > 0)
- {
- ImageInfos = ImageInfos.Except(deleted).ToArray();
- }
- }
-
- 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 imageUpdated || newImageList.Count > 0;
- }
-
- private ItemImageInfo GetImageInfo(FileSystemMetadata file, ImageType type)
- {
- return new ItemImageInfo
- {
- Path = file.FullName,
- Type = type,
- DateModified = FileSystem.GetLastWriteTimeUtc(file)
- };
- }
-
- /// <summary>
- /// Gets the file system path to delete when the item is to be deleted
- /// </summary>
- /// <returns></returns>
- public virtual IEnumerable<FileSystemMetadata> GetDeletePaths()
- {
- return new[] {
- new FileSystemMetadata
- {
- FullName = Path,
- IsDirectory = IsFolder
- }
- }.Concat(GetLocalMetadataFilesToDelete());
- }
-
- protected List<FileSystemMetadata> GetLocalMetadataFilesToDelete()
- {
- if (IsFolder || !IsInMixedFolder)
- {
- return new List<FileSystemMetadata>();
- }
-
- var filename = System.IO.Path.GetFileNameWithoutExtension(Path);
- var extensions = new List<string> { ".nfo", ".xml", ".srt", ".vtt", ".sub", ".idx", ".txt", ".edl" };
- extensions.AddRange(SupportedImageExtensions);
-
- 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();
- }
-
- public bool AllowsMultipleImages(ImageType type)
- {
- return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
- }
-
- public void SwapImages(ImageType type, int index1, int index2)
- {
- if (!AllowsMultipleImages(type))
- {
- throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots");
- }
-
- var info1 = GetImageInfo(type, index1);
- var info2 = GetImageInfo(type, index2);
-
- if (info1 == null || info2 == null)
- {
- // Nothing to do
- return;
- }
-
- if (!info1.IsLocalFile || !info2.IsLocalFile)
- {
- // TODO: Not supported yet
- return;
- }
-
- var path1 = info1.Path;
- var path2 = info2.Path;
-
- FileSystem.SwapFiles(path1, path2);
-
- // Refresh these values
- info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
- info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
-
- info1.Width = 0;
- info1.Height = 0;
- info2.Width = 0;
- info2.Height = 0;
-
- UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
- }
-
- public virtual bool IsPlayed(User user)
- {
- var userdata = UserDataManager.GetUserData(user, this);
-
- return userdata != null && userdata.Played;
- }
-
- public bool IsFavoriteOrLiked(User user)
- {
- var userdata = UserDataManager.GetUserData(user, this);
-
- return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false));
- }
-
- public virtual bool IsUnplayed(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- var userdata = UserDataManager.GetUserData(user, this);
-
- return userdata == null || !userdata.Played;
- }
-
- ItemLookupInfo IHasLookupInfo<ItemLookupInfo>.GetLookupInfo()
- {
- return GetItemLookupInfo<ItemLookupInfo>();
- }
-
- protected T GetItemLookupInfo<T>()
- where T : ItemLookupInfo, new()
- {
- return new T
- {
- MetadataCountryCode = GetPreferredMetadataCountryCode(),
- MetadataLanguage = GetPreferredMetadataLanguage(),
- Name = GetNameForMetadataLookup(),
- ProviderIds = ProviderIds,
- IndexNumber = IndexNumber,
- ParentIndexNumber = ParentIndexNumber,
- Year = ProductionYear,
- PremiereDate = PremiereDate
- };
- }
-
- protected virtual string GetNameForMetadataLookup()
- {
- return Name;
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public virtual bool BeforeMetadataRefresh()
- {
- _sortName = null;
-
- var hasChanges = false;
-
- if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))
- {
- Name = FileSystem.GetFileNameWithoutExtension(Path);
- hasChanges = true;
- }
-
- return hasChanges;
- }
-
- protected static string GetMappedPath(BaseItem item, string path, LocationType locationType)
- {
- if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
- {
- return LibraryManager.GetPathAfterNetworkSubstitution(path, item);
- }
-
- return path;
- }
-
- public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, ItemFields[] fields)
- {
- if (RunTimeTicks.HasValue)
- {
- double pct = RunTimeTicks.Value;
-
- if (pct > 0)
- {
- pct = userData.PlaybackPositionTicks / pct;
-
- if (pct > 0)
- {
- dto.PlayedPercentage = 100 * pct;
- }
- }
- }
- }
-
- protected Task RefreshMetadataForOwnedItem(BaseItem ownedItem, bool copyTitleMetadata, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- var newOptions = new MetadataRefreshOptions(options);
- newOptions.SearchResult = null;
-
- var item = this;
-
- if (copyTitleMetadata)
- {
- // Take some data from the main item, for querying purposes
- if (!item.Genres.SequenceEqual(ownedItem.Genres, StringComparer.Ordinal))
- {
- newOptions.ForceSave = true;
- ownedItem.Genres = item.Genres.ToList();
- }
- if (!item.Studios.SequenceEqual(ownedItem.Studios, StringComparer.Ordinal))
- {
- newOptions.ForceSave = true;
- ownedItem.Studios = item.Studios;
- }
- if (!item.ProductionLocations.SequenceEqual(ownedItem.ProductionLocations, StringComparer.Ordinal))
- {
- newOptions.ForceSave = true;
- ownedItem.ProductionLocations = item.ProductionLocations;
- }
- if (item.CommunityRating != ownedItem.CommunityRating)
- {
- ownedItem.CommunityRating = item.CommunityRating;
- newOptions.ForceSave = true;
- }
- if (item.CriticRating != ownedItem.CriticRating)
- {
- ownedItem.CriticRating = item.CriticRating;
- newOptions.ForceSave = true;
- }
- if (!string.Equals(item.Overview, ownedItem.Overview, StringComparison.Ordinal))
- {
- ownedItem.Overview = item.Overview;
- newOptions.ForceSave = true;
- }
- if (!string.Equals(item.OfficialRating, ownedItem.OfficialRating, StringComparison.Ordinal))
- {
- ownedItem.OfficialRating = item.OfficialRating;
- newOptions.ForceSave = true;
- }
- if (!string.Equals(item.CustomRating, ownedItem.CustomRating, StringComparison.Ordinal))
- {
- ownedItem.CustomRating = item.CustomRating;
- newOptions.ForceSave = true;
- }
- }
-
- return ownedItem.RefreshMetadata(newOptions, cancellationToken);
- }
-
- protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, bool copyTitleMetadata, string path, CancellationToken cancellationToken)
- {
- var newOptions = new MetadataRefreshOptions(options);
- newOptions.SearchResult = null;
-
- var id = LibraryManager.GetNewItemId(path, typeof(Video));
-
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var video = LibraryManager.GetItemById(id) as Video;
-
- if (video == null)
- {
- video = LibraryManager.ResolvePath(FileSystem.GetFileSystemInfo(path)) as Video;
-
- newOptions.ForceSave = true;
- }
-
- //var parentId = Id;
- //if (!video.IsOwnedItem || video.ParentId != parentId)
- //{
- // video.IsOwnedItem = true;
- // video.ParentId = parentId;
- // newOptions.ForceSave = true;
- //}
-
- if (video == null)
- {
- return Task.FromResult(true);
- }
-
- return RefreshMetadataForOwnedItem(video, copyTitleMetadata, newOptions, cancellationToken);
- }
-
- public string GetEtag(User user)
- {
- var list = GetEtagValues(user);
-
- return string.Join("|", list.ToArray(list.Count)).GetMD5().ToString("N");
- }
-
- protected virtual List<string> GetEtagValues(User user)
- {
- return new List<string>
- {
- DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
- };
- }
-
- public virtual IEnumerable<Guid> GetAncestorIds()
- {
- return GetParents().Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id));
- }
-
- public BaseItem GetTopParent()
- {
- if (IsTopParent)
- {
- return this;
- }
-
- foreach (var parent in GetParents())
- {
- if (parent.IsTopParent)
- {
- return parent;
- }
- }
- return null;
- }
-
- [IgnoreDataMember]
- public virtual bool IsTopParent
- {
- get
- {
- if (this is BasePluginFolder || this is Channel)
- {
- return true;
- }
-
- var view = this as UserView;
- if (view != null)
- {
- if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- if (string.Equals(view.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- if (GetParent() is AggregateFolder)
- {
- return true;
- }
-
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsAncestors
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool StopRefreshIfLocalMetadataFound
- {
- get
- {
- return true;
- }
- }
-
- public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
- {
- return new[] { Id };
- }
-
- public virtual Task Delete(DeleteOptions options)
- {
- return LibraryManager.DeleteItem(this, options);
- }
-
- public virtual List<ExternalUrl> GetRelatedUrls()
- {
- return new List<ExternalUrl>();
- }
-
- public virtual double? GetRefreshProgress()
- {
- return null;
- }
-
- public virtual ItemUpdateType OnMetadataChanged()
- {
- var updateType = ItemUpdateType.None;
-
- var item = this;
-
- var inheritedParentalRatingValue = item.GetInheritedParentalRatingValue() ?? 0;
- if (inheritedParentalRatingValue != item.InheritedParentalRatingValue)
- {
- item.InheritedParentalRatingValue = inheritedParentalRatingValue;
- updateType |= ItemUpdateType.MetadataImport;
- }
-
- return updateType;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
deleted file mode 100644
index c06f1cef4..000000000
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Plugins derive from and export this class to create a folder that will appear in the root along
- /// with all the other actual physical folders in the system.
- /// </summary>
- public abstract class BasePluginFolder : Folder, ICollectionFolder
- {
- [IgnoreDataMember]
- public virtual string CollectionType
- {
- get { return null; }
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- //public override double? GetDefaultPrimaryImageAspectRatio()
- //{
- // double value = 16;
- // value /= 9;
-
- // return value;
- //}
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
deleted file mode 100644
index 45e3915ce..000000000
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Linq;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class Book : BaseItem, IHasLookupInfo<BookInfo>, IHasSeries
- {
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Book;
- }
- }
-
- [IgnoreDataMember]
- public string SeriesPresentationUniqueKey { get; set; }
- [IgnoreDataMember]
- public string SeriesName { get; set; }
- [IgnoreDataMember]
- public Guid? SeriesId { get; set; }
-
- public string FindSeriesSortName()
- {
- return SeriesName;
- }
- public string FindSeriesName()
- {
- return SeriesName;
- }
- public string FindSeriesPresentationUniqueKey()
- {
- return SeriesPresentationUniqueKey;
- }
-
- public Guid? FindSeriesId()
- {
- return SeriesId;
- }
-
- public override bool CanDownload()
- {
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Book;
- }
-
- public BookInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<BookInfo>();
-
- if (string.IsNullOrEmpty(SeriesName))
- {
- info.SeriesName = GetParents().Select(i => i.Name).FirstOrDefault();
- }
- else
- {
- info.SeriesName = SeriesName;
- }
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
deleted file mode 100644
index 03fca60c8..000000000
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ /dev/null
@@ -1,387 +0,0 @@
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Specialized Folder class that points to a subset of the physical folders in the system.
- /// It is created from the user-specific folders within the system root
- /// </summary>
- public class CollectionFolder : Folder, ICollectionFolder
- {
- public static IXmlSerializer XmlSerializer { get; set; }
-
- public CollectionFolder()
- {
- PhysicalLocationsList = EmptyStringArray;
- PhysicalFolderIds = EmptyGuidArray;
- }
-
- //public override double? GetDefaultPrimaryImageAspectRatio()
- //{
- // double value = 16;
- // value /= 9;
-
- // return value;
- //}
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public string CollectionType { get; set; }
-
- private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>();
- public LibraryOptions GetLibraryOptions()
- {
- return GetLibraryOptions(Path);
- }
-
- private static LibraryOptions LoadLibraryOptions(string path)
- {
- try
- {
- var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions;
-
- if (result == null)
- {
- return new LibraryOptions();
- }
-
- return result;
- }
- catch (FileNotFoundException)
- {
- return new LibraryOptions();
- }
- catch (IOException)
- {
- return new LibraryOptions();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error loading library options", ex);
-
- return new LibraryOptions();
- }
- }
-
- private static string GetLibraryOptionsPath(string path)
- {
- return System.IO.Path.Combine(path, "options.xml");
- }
-
- public void UpdateLibraryOptions(LibraryOptions options)
- {
- SaveLibraryOptions(Path, options);
- }
-
- public static LibraryOptions GetLibraryOptions(string path)
- {
- lock (LibraryOptions)
- {
- LibraryOptions options;
- if (!LibraryOptions.TryGetValue(path, out options))
- {
- options = LoadLibraryOptions(path);
- LibraryOptions[path] = options;
- }
-
- return options;
- }
- }
-
- public static void SaveLibraryOptions(string path, LibraryOptions options)
- {
- lock (LibraryOptions)
- {
- LibraryOptions[path] = options;
-
- XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
- }
- }
-
- /// <summary>
- /// Allow different display preferences for each collection folder
- /// </summary>
- /// <value>The display prefs id.</value>
- [IgnoreDataMember]
- public override Guid DisplayPreferencesId
- {
- get
- {
- return Id;
- }
- }
-
- [IgnoreDataMember]
- public override string[] PhysicalLocations
- {
- get
- {
- return PhysicalLocationsList;
- }
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- public string[] PhysicalLocationsList { get; set; }
- public Guid[] PhysicalFolderIds { get; set; }
-
- protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
- {
- return CreateResolveArgs(directoryService, true).FileSystemChildren;
- }
-
- private bool _requiresRefresh;
- public override bool RequiresRefresh()
- {
- var changed = base.RequiresRefresh() || _requiresRefresh;
-
- if (!changed)
- {
- var locations = PhysicalLocations;
-
- var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations;
-
- if (!locations.SequenceEqual(newLocations))
- {
- changed = true;
- }
- }
-
- if (!changed)
- {
- var folderIds = PhysicalFolderIds;
-
- var newFolderIds = GetPhysicalFolders(false).Select(i => i.Id).ToList();
-
- if (!folderIds.SequenceEqual(newFolderIds))
- {
- changed = true;
- }
- }
-
- return changed;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var changed = base.BeforeMetadataRefresh() || _requiresRefresh;
- _requiresRefresh = false;
- return changed;
- }
-
- public override double? GetRefreshProgress()
- {
- var folders = GetPhysicalFolders(true).ToList();
- double totalProgresses = 0;
- var foldersWithProgress = 0;
-
- foreach (var folder in folders)
- {
- var progress = ProviderManager.GetRefreshProgress(folder.Id);
- if (progress.HasValue)
- {
- totalProgresses += progress.Value;
- foldersWithProgress++;
- }
- }
-
- if (foldersWithProgress == 0)
- {
- return null;
- }
-
- return (totalProgresses / foldersWithProgress);
- }
-
- protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
- {
- return RefreshLinkedChildrenInternal(true);
- }
-
- private bool RefreshLinkedChildrenInternal(bool setFolders)
- {
- var physicalFolders = GetPhysicalFolders(false)
- .ToList();
-
- var linkedChildren = physicalFolders
- .SelectMany(c => c.LinkedChildren)
- .ToList();
-
- var changed = !linkedChildren.SequenceEqual(LinkedChildren, new LinkedChildComparer(FileSystem));
-
- LinkedChildren = linkedChildren.ToArray(linkedChildren.Count);
-
- var folderIds = PhysicalFolderIds;
- var newFolderIds = physicalFolders.Select(i => i.Id).ToArray();
-
- if (!folderIds.SequenceEqual(newFolderIds))
- {
- changed = true;
- if (setFolders)
- {
- PhysicalFolderIds = newFolderIds;
- }
- }
-
- return changed;
- }
-
- private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations)
- {
- var path = ContainingFolderPath;
-
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
- {
- FileInfo = FileSystem.GetDirectoryInfo(path),
- Path = path,
- Parent = GetParent() as Folder,
- CollectionType = CollectionType
- };
-
- // Gather child folder and files
- if (args.IsDirectory)
- {
- var isPhysicalRoot = args.IsPhysicalRoot;
-
- // When resolving the root, we need it's grandchildren (children of user views)
- var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
-
- var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
-
- // Need to remove subpaths that may have been resolved from shortcuts
- // Example: if \\server\movies exists, then strip out \\server\movies\action
- if (isPhysicalRoot)
- {
- files = LibraryManager.NormalizeRootPathList(files).ToArray();
- }
-
- args.FileSystemChildren = files;
- }
-
- _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations);
- if (setPhysicalLocations)
- {
- PhysicalLocationsList = args.PhysicalLocations;
- }
-
- return args;
- }
-
- /// <summary>
- /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes
- /// ***Currently does not contain logic to maintain items that are unavailable in the file system***
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param>
- /// <param name="refreshOptions">The refresh options.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns>Task.</returns>
- protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- return Task.FromResult(true);
- }
-
- /// <summary>
- /// Our children are actually just references to the ones in the physical root...
- /// </summary>
- /// <value>The actual children.</value>
- [IgnoreDataMember]
- public override IEnumerable<BaseItem> Children
- {
- get { return GetActualChildren(); }
- }
-
- public IEnumerable<BaseItem> GetActualChildren()
- {
- return GetPhysicalFolders(true).SelectMany(c => c.Children);
- }
-
- public IEnumerable<Folder> GetPhysicalFolders()
- {
- return GetPhysicalFolders(true);
- }
-
- private IEnumerable<Folder> GetPhysicalFolders(bool enableCache)
- {
- if (enableCache)
- {
- return PhysicalFolderIds.Select(i => LibraryManager.GetItemById(i)).OfType<Folder>();
- }
-
- var rootChildren = LibraryManager.RootFolder.Children
- .OfType<Folder>()
- .ToList();
-
- return PhysicalLocations.Where(i => !FileSystem.AreEqual(i, Path)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id);
- }
-
- private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren)
- {
- var result = rootChildren
- .Where(i => FileSystem.AreEqual(i.Path, path))
- .ToList();
-
- if (result.Count == 0)
- {
- var folder = LibraryManager.FindByPath(path, true) as Folder;
-
- if (folder != null)
- {
- result.Add(folder);
- }
- }
-
- return result;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs b/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs
deleted file mode 100644
index 80ba206cc..000000000
--- a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Model.Configuration;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
- public static class DayOfWeekHelper
- {
- public static List<DayOfWeek> GetDaysOfWeek(DynamicDayOfWeek day)
- {
- return GetDaysOfWeek(new List<DynamicDayOfWeek> { day });
- }
-
- public static List<DayOfWeek> GetDaysOfWeek(List<DynamicDayOfWeek> days)
- {
- var list = new List<DayOfWeek>();
-
- if (days.Contains(DynamicDayOfWeek.Sunday) ||
- days.Contains(DynamicDayOfWeek.Weekend) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Sunday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Saturday) ||
- days.Contains(DynamicDayOfWeek.Weekend) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Saturday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Monday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Monday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Monday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Tuesday
- );
- }
-
- if (days.Contains(DynamicDayOfWeek.Wednesday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Wednesday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Thursday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Thursday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Friday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Friday);
- }
-
- return list;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs
deleted file mode 100644
index 36855a86c..000000000
--- a/MediaBrowser.Controller/Entities/Extensions.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Linq;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Extensions
- /// </summary>
- public static class Extensions
- {
- /// <summary>
- /// Adds the trailer URL.
- /// </summary>
- public static void AddTrailerUrl(this IHasTrailers item, string url)
- {
- if (string.IsNullOrWhiteSpace(url))
- {
- throw new ArgumentNullException("url");
- }
-
- var current = item.RemoteTrailers.FirstOrDefault(i => string.Equals(i.Url, url, StringComparison.OrdinalIgnoreCase));
-
- if (current == null)
- {
- 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
deleted file mode 100644
index 08b6a123d..000000000
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ /dev/null
@@ -1,1573 +0,0 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Folder
- /// </summary>
- public class Folder : BaseItem
- {
- public static IUserManager UserManager { get; set; }
- public static IUserViewManager UserViewManager { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is root.
- /// </summary>
- /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
- public bool IsRoot { get; set; }
-
- public LinkedChild[] LinkedChildren { get; set; }
-
- [IgnoreDataMember]
- public DateTime? DateLastMediaAdded { get; set; }
-
- public Folder()
- {
- LinkedChildren = EmptyLinkedChildArray;
- }
-
- [IgnoreDataMember]
- public override bool SupportsThemeMedia
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public virtual bool IsPreSorted
- {
- get { return false; }
- }
-
- [IgnoreDataMember]
- public virtual bool IsPhysicalRoot
- {
- get { return false; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is folder.
- /// </summary>
- /// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public override bool IsFolder
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsDisplayedAsFolder
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsCumulativeRunTimeTicks
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsDateLastMediaAdded
- {
- get
- {
- return false;
- }
- }
-
- public override bool CanDelete()
- {
- if (IsRoot)
- {
- return false;
- }
-
- return base.CanDelete();
- }
-
- public override bool RequiresRefresh()
- {
- var baseResult = base.RequiresRefresh();
-
- if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue)
- {
- baseResult = true;
- }
-
- return baseResult;
- }
-
- [IgnoreDataMember]
- public override string FileNameWithoutExtension
- {
- get
- {
- if (LocationType == LocationType.FileSystem)
- {
- return System.IO.Path.GetFileName(Path);
- }
-
- return null;
- }
- }
-
- protected override bool IsAllowTagFilterEnforced()
- {
- if (this is ICollectionFolder)
- {
- return false;
- }
- if (this is UserView)
- {
- return false;
- }
- return true;
- }
-
- [IgnoreDataMember]
- protected virtual bool SupportsShortcutChildren
- {
- get { return false; }
- }
-
- /// <summary>
- /// Adds the child.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.InvalidOperationException">Unable to add + item.Name</exception>
- public void AddChild(BaseItem item, CancellationToken cancellationToken)
- {
- item.SetParent(this);
-
- if (item.Id == Guid.Empty)
- {
- item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
- }
-
- if (Children.Any(i => i.Id == item.Id))
- {
- throw new ArgumentException(string.Format("A child with the Id {0} already exists.", item.Id));
- }
-
- if (item.DateCreated == DateTime.MinValue)
- {
- item.DateCreated = DateTime.UtcNow;
- }
- if (item.DateModified == DateTime.MinValue)
- {
- item.DateModified = DateTime.UtcNow;
- }
-
- LibraryManager.CreateItem(item, cancellationToken);
- }
-
- /// <summary>
- /// Removes the child.
- /// </summary>
- /// <param name="item">The item.</param>
- public void RemoveChild(BaseItem item)
- {
- item.SetParent(null);
- }
-
- /// <summary>
- /// Gets the actual children.
- /// </summary>
- /// <value>The actual children.</value>
- [IgnoreDataMember]
- public virtual IEnumerable<BaseItem> Children
- {
- get
- {
- return LoadChildren();
- }
- }
-
- /// <summary>
- /// thread-safe access to all recursive children of this folder - without regard to user
- /// </summary>
- /// <value>The recursive children.</value>
- [IgnoreDataMember]
- public IEnumerable<BaseItem> RecursiveChildren
- {
- get { return GetRecursiveChildren(); }
- }
-
- public override bool IsVisible(User user)
- {
- if (this is ICollectionFolder && !(this is BasePluginFolder))
- {
- if (user.Policy.BlockedMediaFolders != null)
- {
- if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
-
- // Backwards compatibility
- user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
- else
- {
- if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
- }
-
- return base.IsVisible(user);
- }
-
- /// <summary>
- /// Loads our children. Validation will occur externally.
- /// We want this sychronous.
- /// </summary>
- protected virtual List<BaseItem> LoadChildren()
- {
- //Logger.Debug("Loading children from {0} {1} {2}", GetType().Name, Id, Path);
- //just load our children from the repo - the library will be validated and maintained in other processes
- return GetCachedChildren();
- }
-
- public override double? GetRefreshProgress()
- {
- return ProviderManager.GetRefreshProgress(Id);
- }
-
- public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)));
- }
-
- /// <summary>
- /// Validates that the children of the folder still exist
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="metadataRefreshOptions">The metadata refresh options.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>Task.</returns>
- public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true)
- {
- return ValidateChildrenInternal(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService);
- }
-
- private Dictionary<Guid, BaseItem> GetActualChildrenDictionary()
- {
- var dictionary = new Dictionary<Guid, BaseItem>();
-
- var childrenList = Children.ToList();
-
- foreach (var child in childrenList)
- {
- var id = child.Id;
- if (dictionary.ContainsKey(id))
- {
- Logger.Error("Found folder containing items with duplicate id. Path: {0}, Child Name: {1}",
- Path ?? Name,
- child.Path ?? child.Name);
- }
- else
- {
- dictionary[id] = child;
- }
- }
-
- return dictionary;
- }
-
- protected override void TriggerOnRefreshStart()
- {
- }
-
- protected override void TriggerOnRefreshComplete()
- {
- }
-
- /// <summary>
- /// Validates the children internal.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param>
- /// <param name="refreshOptions">The refresh options.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns>Task.</returns>
- protected virtual async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- if (recursive)
- {
- ProviderManager.OnRefreshStart(this);
- }
-
- try
- {
- await ValidateChildrenInternal2(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService).ConfigureAwait(false);
- }
- finally
- {
- if (recursive)
- {
- ProviderManager.OnRefreshComplete(this);
- }
- }
- }
-
- private async Task ValidateChildrenInternal2(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- var locationType = LocationType;
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var validChildren = new List<BaseItem>();
- var validChildrenNeedGeneration = false;
-
- var allLibraryPaths = LibraryManager
- .GetVirtualFolders()
- .SelectMany(i => i.Locations)
- .ToList();
-
- if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
- {
- IEnumerable<BaseItem> nonCachedChildren;
-
- try
- {
- nonCachedChildren = GetNonCachedChildren(directoryService);
- }
- catch (IOException ex)
- {
- nonCachedChildren = new BaseItem[] { };
-
- Logger.ErrorException("Error getting file system entries for {0}", ex, Path);
- }
-
- if (nonCachedChildren == null) return; //nothing to validate
-
- progress.Report(5);
-
- if (recursive)
- {
- ProviderManager.OnRefreshProgress(this, 5);
- }
-
- //build a dictionary of the current children we have now by Id so we can compare quickly and easily
- var currentChildren = GetActualChildrenDictionary();
-
- //create a list for our validated children
- var newItems = new List<BaseItem>();
-
- cancellationToken.ThrowIfCancellationRequested();
-
- foreach (var child in nonCachedChildren)
- {
- BaseItem currentChild;
-
- if (currentChildren.TryGetValue(child.Id, out currentChild))
- {
- validChildren.Add(currentChild);
-
- if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None)
- {
- currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
- }
-
- continue;
- }
-
- // Brand new item - needs to be added
- child.SetParent(this);
- newItems.Add(child);
- validChildren.Add(child);
- }
-
- // If any items were added or removed....
- if (newItems.Count > 0 || currentChildren.Count != validChildren.Count)
- {
- // That's all the new and changed ones - now see if there are any that are missing
- var itemsRemoved = currentChildren.Values.Except(validChildren).ToList();
- var actualRemovals = new List<BaseItem>();
-
- foreach (var item in itemsRemoved)
- {
- var itemLocationType = item.LocationType;
- if (itemLocationType == LocationType.Virtual ||
- itemLocationType == LocationType.Remote)
- {
- }
-
- else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path, allLibraryPaths))
- {
- }
- else
- {
- actualRemovals.Add(item);
- }
- }
-
- if (actualRemovals.Count > 0)
- {
- foreach (var item in actualRemovals)
- {
- Logger.Debug("Removed item: " + item.Path);
-
- item.SetParent(null);
- await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false);
- LibraryManager.ReportItemRemoved(item, this);
- }
- }
-
- LibraryManager.CreateItems(newItems, this, cancellationToken);
- }
- }
- else
- {
- validChildrenNeedGeneration = true;
- }
-
- progress.Report(10);
-
- if (recursive)
- {
- ProviderManager.OnRefreshProgress(this, 10);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (recursive)
- {
- using (var innerProgress = new ActionableProgress<double>())
- {
- var folder = this;
- innerProgress.RegisterAction(p =>
- {
- double newPct = .80 * p + 10;
- progress.Report(newPct);
- ProviderManager.OnRefreshProgress(folder, newPct);
- });
-
- if (validChildrenNeedGeneration)
- {
- validChildren = Children.ToList();
- validChildrenNeedGeneration = false;
- }
-
- await ValidateSubFolders(validChildren.OfType<Folder>().ToList(), directoryService, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- }
-
- if (refreshChildMetadata)
- {
- progress.Report(90);
-
- if (recursive)
- {
- ProviderManager.OnRefreshProgress(this, 90);
- }
-
- var container = this as IMetadataContainer;
-
- using (var innerProgress = new ActionableProgress<double>())
- {
- var folder = this;
- innerProgress.RegisterAction(p =>
- {
- double newPct = .10 * p + 90;
- progress.Report(newPct);
- if (recursive)
- {
- ProviderManager.OnRefreshProgress(folder, newPct);
- }
- });
-
- if (container != null)
- {
- await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- if (validChildrenNeedGeneration)
- {
- validChildren = Children.ToList();
- }
-
- await RefreshMetadataRecursive(validChildren, refreshOptions, recursive, innerProgress, cancellationToken);
- }
- }
- }
- }
-
- private async Task RefreshMetadataRecursive(List<BaseItem> children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var numComplete = 0;
- var count = children.Count;
- double currentPercent = 0;
-
- foreach (var child in children)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var innerProgress = new ActionableProgress<double>())
- {
- // Avoid implicitly captured closure
- var currentInnerPercent = currentPercent;
-
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = currentInnerPercent;
- innerPercent += p / (count);
- progress.Report(innerPercent);
- });
-
- await RefreshChildMetadata(child, refreshOptions, recursive && child.IsFolder, innerProgress, cancellationToken)
- .ConfigureAwait(false);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
- currentPercent = percent;
-
- progress.Report(percent);
- }
- }
-
- private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var container = child as IMetadataContainer;
-
- if (container != null)
- {
- await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- if (refreshOptions.RefreshItem(child))
- {
- await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
-
- if (recursive)
- {
- var folder = child as Folder;
-
- if (folder != null)
- {
- await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken);
- }
- }
- }
- }
-
- /// <summary>
- /// Refreshes the children.
- /// </summary>
- /// <param name="children">The children.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var numComplete = 0;
- var count = children.Count;
- double currentPercent = 0;
-
- foreach (var child in children)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var innerProgress = new ActionableProgress<double>())
- {
- // Avoid implicitly captured closure
- var currentInnerPercent = currentPercent;
-
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = currentInnerPercent;
- innerPercent += p / (count);
- progress.Report(innerPercent);
- });
-
- await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
- .ConfigureAwait(false);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
- currentPercent = percent;
-
- progress.Report(percent);
- }
- }
-
- public static bool IsPathOffline(string path, List<string> allLibraryPaths)
- {
- //if (FileSystem.FileExists(path))
- //{
- // return false;
- //}
-
- var originalPath = path;
-
- // Depending on whether the path is local or unc, it may return either null or '\' at the top
- while (!string.IsNullOrWhiteSpace(path) && path.Length > 1)
- {
- if (FileSystem.DirectoryExists(path))
- {
- return false;
- }
-
- if (allLibraryPaths.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
-
- path = FileSystem.GetDirectoryName(path);
- }
-
- return allLibraryPaths.Any(i => ContainsPath(i, originalPath));
- }
-
- private static bool ContainsPath(string parent, string path)
- {
- return FileSystem.AreEqual(parent, path) || FileSystem.ContainsSubPath(parent, path);
- }
-
- /// <summary>
- /// Get the children of this folder from the actual file system
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- var collectionType = LibraryManager.GetContentType(this);
- var libraryOptions = LibraryManager.GetLibraryOptions(this);
-
- return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, libraryOptions, collectionType);
- }
-
- /// <summary>
- /// Get our children from the repo - stubbed for now
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- protected List<BaseItem> GetCachedChildren()
- {
- return ItemRepository.GetItemList(new InternalItemsQuery
- {
- Parent = this,
- GroupByPresentationUniqueKey = false,
- DtoOptions = new DtoOptions(true)
- });
- }
-
- public virtual int GetChildCount(User user)
- {
- if (LinkedChildren.Length > 0)
- {
- if (!(this is ICollectionFolder))
- {
- return GetChildren(user, true).Count;
- }
- }
-
- var result = GetItems(new InternalItemsQuery(user)
- {
- Recursive = false,
- Limit = 0,
- Parent = this,
- DtoOptions = new DtoOptions(false)
- {
- EnableImages = false
- }
-
- });
-
- return result.TotalRecordCount;
- }
-
- public virtual int GetRecursiveChildCount(User user)
- {
- return GetItems(new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false,
- IsVirtualItem = false,
- EnableTotalRecordCount = true,
- Limit = 0,
- DtoOptions = new DtoOptions(false)
- {
- EnableImages = false
- }
-
- }).TotalRecordCount;
- }
-
- public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query)
- {
- var user = query.User;
-
- if (!query.ForceDirect && RequiresPostFiltering(query))
- {
- IEnumerable<BaseItem> items;
- Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
-
- if (query.User == null)
- {
- items = GetRecursiveChildren(filter);
- }
- else
- {
- items = GetRecursiveChildren(user, query);
- }
-
- return PostFilterAndSort(items, query, true, true);
- }
-
- if (!(this is UserRootFolder) && !(this is AggregateFolder))
- {
- if (!query.ParentId.HasValue)
- {
- query.Parent = this;
- }
- }
-
- if (RequiresPostFiltering2(query))
- {
- return QueryWithPostFiltering2(query);
- }
-
- return LibraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> QueryWithPostFiltering2(InternalItemsQuery query)
- {
- var startIndex = query.StartIndex;
- var limit = query.Limit;
-
- query.StartIndex = null;
- query.Limit = null;
-
- var itemsList = LibraryManager.GetItemList(query);
- var user = query.User;
-
- if (user != null)
- {
- // needed for boxsets
- itemsList = itemsList.Where(i => i.IsVisibleStandalone(query.User)).ToList();
- }
-
- BaseItem[] returnItems;
- int totalCount = 0;
-
- if (query.EnableTotalRecordCount)
- {
- var itemsArray = itemsList.ToArray();
- totalCount = itemsArray.Length;
- returnItems = itemsArray;
- }
- else
- {
- returnItems = itemsList.ToArray();
- }
-
- if (limit.HasValue)
- {
- returnItems = returnItems.Skip(startIndex ?? 0).Take(limit.Value).ToArray();
- }
- else if (startIndex.HasValue)
- {
- returnItems = returnItems.Skip(startIndex.Value).ToArray();
- }
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = returnItems.ToArray()
- };
- }
-
- private bool RequiresPostFiltering2(InternalItemsQuery query)
- {
- if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], typeof(BoxSet).Name, StringComparison.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to BoxSet query");
- return true;
- }
-
- return false;
- }
-
- private bool RequiresPostFiltering(InternalItemsQuery query)
- {
- if (LinkedChildren.Length > 0)
- {
- if (!(this is ICollectionFolder))
- {
- Logger.Debug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name);
- return true;
- }
- }
-
- if (query.IsInBoxSet.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsInBoxSet");
- return true;
- }
-
- // Filter by Video3DFormat
- if (query.Is3D.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to Is3D");
- return true;
- }
-
- if (query.HasOfficialRating.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to HasOfficialRating");
- return true;
- }
-
- if (query.IsPlaceHolder.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsPlaceHolder");
- return true;
- }
-
- if (query.HasSpecialFeature.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to HasSpecialFeature");
- return true;
- }
-
- if (query.HasSubtitles.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to HasSubtitles");
- return true;
- }
-
- if (query.HasTrailer.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to HasTrailer");
- return true;
- }
-
- // Filter by VideoType
- if (query.VideoTypes.Length > 0)
- {
- Logger.Debug("Query requires post-filtering due to VideoTypes");
- return true;
- }
-
- // Apply person filter
- if (query.ItemIdsFromPersonFilters != null)
- {
- Logger.Debug("Query requires post-filtering due to ItemIdsFromPersonFilters");
- return true;
- }
-
- if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User, ConfigurationManager))
- {
- Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems");
- return true;
- }
-
- if (!string.IsNullOrWhiteSpace(query.AdjacentTo))
- {
- Logger.Debug("Query requires post-filtering due to AdjacentTo");
- return true;
- }
-
- if (query.SeriesStatuses.Length > 0)
- {
- Logger.Debug("Query requires post-filtering due to SeriesStatuses");
- return true;
- }
-
- if (query.AiredDuringSeason.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to AiredDuringSeason");
- return true;
- }
-
- if (query.IsPlayed.HasValue)
- {
- if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(typeof(Series).Name))
- {
- Logger.Debug("Query requires post-filtering due to IsPlayed");
- return true;
- }
- }
-
- return false;
- }
-
- public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
- {
- if (query.ItemIds.Length > 0)
- {
- var result = LibraryManager.GetItemsResult(query);
-
- if (query.OrderBy.Length == 0)
- {
- var ids = query.ItemIds.ToList();
-
- // Try to preserve order
- result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
- }
- return result;
- }
-
- return GetItemsInternal(query);
- }
-
- public BaseItem[] GetItemList(InternalItemsQuery query)
- {
- query.EnableTotalRecordCount = false;
-
- if (query.ItemIds.Length > 0)
- {
- var result = LibraryManager.GetItemList(query);
-
- if (query.OrderBy.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)
- {
- try
- {
- // Don't blow up here because it could cause parent screens with other content to fail
- return ChannelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = ChannelId,
- FolderId = Id.ToString("N"),
- Limit = query.Limit,
- StartIndex = query.StartIndex,
- UserId = query.User.Id.ToString("N"),
- OrderBy = query.OrderBy
-
- }, new SimpleProgress<double>(), CancellationToken.None).Result;
- }
- catch
- {
- // Already logged at lower levels
- return new QueryResult<BaseItem>();
- }
- }
-
- if (query.Recursive)
- {
- return QueryRecursive(query);
- }
-
- var user = query.User;
-
- Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
-
- IEnumerable<BaseItem> items;
-
- if (query.User == null)
- {
- items = query.Recursive
- ? GetRecursiveChildren(filter)
- : Children.Where(filter);
- }
- else
- {
- items = query.Recursive
- ? GetRecursiveChildren(user, query)
- : GetChildren(user, true).Where(filter);
- }
-
- return PostFilterAndSort(items, query, true, true);
- }
-
- protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query, bool collapseBoxSetItems, bool enableSorting)
- {
- return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager, collapseBoxSetItems, enableSorting);
- }
-
- public virtual List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
-
- //the true root should return our users root folder children
- if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren);
-
- var result = new Dictionary<Guid, BaseItem>();
-
- AddChildren(user, includeLinkedChildren, result, false, null);
-
- return result.Values.ToList();
- }
-
- protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
- {
- return Children;
- }
-
- /// <summary>
- /// Adds the children to list.
- /// </summary>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, InternalItemsQuery query)
- {
- foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
- {
- if (child.IsVisible(user))
- {
- if (query == null || UserViewBuilder.FilterItem(child, query))
- {
- result[child.Id] = child;
- }
-
- if (recursive && child.IsFolder)
- {
- var folder = (Folder)child;
-
- folder.AddChildren(user, includeLinkedChildren, result, true, query);
- }
- }
- }
-
- if (includeLinkedChildren)
- {
- foreach (var child in GetLinkedChildren(user))
- {
- if (child.IsVisible(user))
- {
- if (query == null || UserViewBuilder.FilterItem(child, query))
- {
- result[child.Id] = child;
- }
- }
- }
- }
- }
-
- /// <summary>
- /// Gets allowed recursive children of an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
- /// <returns>IEnumerable{BaseItem}.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
- {
- return GetRecursiveChildren(user, null);
- }
-
- public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- var result = new Dictionary<Guid, BaseItem>();
-
- AddChildren(user, true, result, true, query);
-
- return result.Values;
- }
-
- /// <summary>
- /// Gets the recursive children.
- /// </summary>
- /// <returns>IList{BaseItem}.</returns>
- public IList<BaseItem> GetRecursiveChildren()
- {
- return GetRecursiveChildren(true);
- }
-
- public IList<BaseItem> GetRecursiveChildren(bool includeLinkedChildren)
- {
- return GetRecursiveChildren(i => true, includeLinkedChildren);
- }
-
- public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
- {
- return GetRecursiveChildren(filter, true);
- }
-
- public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter, bool includeLinkedChildren)
- {
- var result = new Dictionary<Guid, BaseItem>();
-
- AddChildrenToList(result, includeLinkedChildren, true, filter);
-
- return result.Values.ToList();
- }
-
- /// <summary>
- /// Adds the children to list.
- /// </summary>
- private void AddChildrenToList(Dictionary<Guid, BaseItem> result, bool includeLinkedChildren, bool recursive, Func<BaseItem, bool> filter)
- {
- foreach (var child in Children)
- {
- if (filter == null || filter(child))
- {
- result[child.Id] = child;
- }
-
- if (recursive && child.IsFolder)
- {
- var folder = (Folder)child;
-
- // We can only support includeLinkedChildren for the first folder, or we might end up stuck in a loop of linked items
- folder.AddChildrenToList(result, false, true, filter);
- }
- }
-
- if (includeLinkedChildren)
- {
- foreach (var child in GetLinkedChildren())
- {
- if (filter == null || filter(child))
- {
- result[child.Id] = child;
- }
- }
- }
- }
-
-
- /// <summary>
- /// Gets the linked children.
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- public List<BaseItem> GetLinkedChildren()
- {
- var linkedChildren = LinkedChildren;
- var list = new List<BaseItem>(linkedChildren.Length);
-
- foreach (var i in linkedChildren)
- {
- var child = GetLinkedChild(i);
-
- if (child != null)
- {
- list.Add(child);
- }
- }
- return list;
- }
-
- protected virtual bool FilterLinkedChildrenPerUser
- {
- get
- {
- return false;
- }
- }
-
- public List<BaseItem> GetLinkedChildren(User user)
- {
- if (!FilterLinkedChildrenPerUser || user == null)
- {
- return GetLinkedChildren();
- }
-
- var linkedChildren = LinkedChildren;
- var list = new List<BaseItem>(linkedChildren.Length);
-
- if (linkedChildren.Length == 0)
- {
- return list;
- }
-
- var allUserRootChildren = user.RootFolder.Children.OfType<Folder>().ToList();
-
- var collectionFolderIds = allUserRootChildren
- .OfType<CollectionFolder>()
- .Where(i => i.IsVisible(user))
- .Select(i => i.Id)
- .ToList();
-
- foreach (var i in linkedChildren)
- {
- var child = GetLinkedChild(i);
-
- if (child == null)
- {
- continue;
- }
-
- var childOwner = child.IsOwnedItem ? (child.GetOwner() ?? child) : child;
-
- if (childOwner != null && !(child is IItemByName))
- {
- var childLocationType = childOwner.LocationType;
- if (childLocationType == LocationType.Remote || childLocationType == LocationType.Virtual)
- {
- if (!childOwner.IsVisibleStandalone(user))
- {
- continue;
- }
- }
- else if (childLocationType == LocationType.FileSystem)
- {
- var itemCollectionFolderIds =
- LibraryManager.GetCollectionFolders(childOwner, allUserRootChildren).Select(f => f.Id);
-
- if (!itemCollectionFolderIds.Any(collectionFolderIds.Contains))
- {
- continue;
- }
- }
- }
-
- list.Add(child);
- }
-
- return list;
- }
-
- /// <summary>
- /// Gets the linked children.
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- public IEnumerable<Tuple<LinkedChild, BaseItem>> GetLinkedChildrenInfos()
- {
- return LinkedChildren
- .Select(i => new Tuple<LinkedChild, BaseItem>(i, GetLinkedChild(i)))
- .Where(i => i.Item2 != null);
- }
-
- [IgnoreDataMember]
- protected override bool SupportsOwnedItems
- {
- get
- {
- return base.SupportsOwnedItems || SupportsShortcutChildren;
- }
- }
-
- protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var changesFound = false;
-
- if (LocationType == LocationType.FileSystem)
- {
- if (RefreshLinkedChildren(fileSystemChildren))
- {
- changesFound = true;
- }
- }
-
- var baseHasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
-
- return baseHasChanges || changesFound;
- }
-
- /// <summary>
- /// Refreshes the linked children.
- /// </summary>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- protected virtual bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
- {
- if (SupportsShortcutChildren)
- {
- var newShortcutLinks = fileSystemChildren
- .Where(i => !i.IsDirectory && FileSystem.IsShortcut(i.FullName))
- .Select(i =>
- {
- try
- {
- Logger.Debug("Found shortcut at {0}", i.FullName);
-
- var resolvedPath = FileSystem.ResolveShortcut(i.FullName);
-
- if (!string.IsNullOrEmpty(resolvedPath))
- {
- return new LinkedChild
- {
- Path = resolvedPath,
- Type = LinkedChildType.Shortcut
- };
- }
-
- Logger.Error("Error resolving shortcut {0}", i.FullName);
-
- return null;
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error resolving shortcut {0}", ex, i.FullName);
- return null;
- }
- })
- .Where(i => i != null)
- .ToList();
-
- var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
-
- 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)
- {
- // Reset the cached value
- child.ItemId = null;
- }
-
- return false;
- }
-
- /// <summary>
- /// Marks the played.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="datePlayed">The date played.</param>
- /// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
- /// <returns>Task.</returns>
- public override void MarkPlayed(User user,
- DateTime? datePlayed,
- bool resetPosition)
- {
- var query = new InternalItemsQuery
- {
- User = user,
- Recursive = true,
- IsFolder = false,
- EnableTotalRecordCount = false
- };
-
- if (!user.Configuration.DisplayMissingEpisodes)
- {
- query.IsVirtualItem = false;
- }
-
- var itemsResult = GetItemList(query);
-
- // Sweep through recursively and update status
- foreach (var item in itemsResult)
- {
- if (item.IsVirtualItem)
- {
- // The querying doesn't support virtual unaired
- var episode = item as Episode;
- if (episode != null && episode.IsUnaired)
- {
- continue;
- }
- }
-
- item.MarkPlayed(user, datePlayed, resetPosition);
- }
- }
-
- /// <summary>
- /// Marks the unplayed.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- public override void MarkUnplayed(User user)
- {
- var itemsResult = GetItemList(new InternalItemsQuery
- {
- User = user,
- Recursive = true,
- IsFolder = false,
- EnableTotalRecordCount = false
-
- });
-
- // Sweep through recursively and update status
- foreach (var item in itemsResult)
- {
- item.MarkUnplayed(user);
- }
- }
-
- public override bool IsPlayed(User user)
- {
- var itemsResult = GetItemList(new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false,
- IsVirtualItem = false,
- EnableTotalRecordCount = false
-
- });
-
- return itemsResult
- .All(i => i.IsPlayed(user));
- }
-
- public override bool IsUnplayed(User user)
- {
- return !IsPlayed(user);
- }
-
- [IgnoreDataMember]
- public virtual bool SupportsUserDataFromChildren
- {
- get
- {
- // These are just far too slow.
- if (this is ICollectionFolder)
- {
- return false;
- }
- if (this is UserView)
- {
- return false;
- }
- if (this is UserRootFolder)
- {
- return false;
- }
- if (this is Channel)
- {
- return false;
- }
- if (SourceType != SourceType.Library)
- {
- return false;
- }
- var iItemByName = this as IItemByName;
- if (iItemByName != null)
- {
- var hasDualAccess = this as IHasDualAccess;
- if (hasDualAccess == null || hasDualAccess.IsAccessedByName)
- {
- return false;
- }
- }
-
- return true;
- }
- }
-
- public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, ItemFields[] fields)
- {
- if (!SupportsUserDataFromChildren)
- {
- return;
- }
-
- if (itemDto != null)
- {
- if (fields.Contains(ItemFields.RecursiveItemCount))
- {
- itemDto.RecursiveItemCount = GetRecursiveChildCount(user);
- }
- }
-
- if (SupportsPlayedStatus)
- {
- var unplayedQueryResult = GetItems(new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false,
- IsVirtualItem = false,
- EnableTotalRecordCount = true,
- Limit = 0,
- IsPlayed = false,
- DtoOptions = new DtoOptions(false)
- {
- EnableImages = false
- }
-
- });
-
- double unplayedCount = unplayedQueryResult.TotalRecordCount;
-
- dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount;
-
- if (itemDto != null && itemDto.RecursiveItemCount.HasValue)
- {
- if (itemDto.RecursiveItemCount.Value > 0)
- {
- var unplayedPercentage = (unplayedCount / itemDto.RecursiveItemCount.Value) * 100;
- dto.PlayedPercentage = 100 - unplayedPercentage;
- dto.Played = dto.PlayedPercentage.Value >= 100;
- }
- }
- else
- {
- dto.Played = (dto.UnplayedItemCount ?? 0) == 0;
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
deleted file mode 100644
index bead0ef95..000000000
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class Game : BaseItem, IHasTrailers, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
- {
- public Game()
- {
- MultiPartGameFiles = EmptyStringArray;
- RemoteTrailers = EmptyMediaUrlArray;
- LocalTrailerIds = EmptyGuidArray;
- RemoteTrailerIds = EmptyGuidArray;
- }
-
- public Guid[] LocalTrailerIds { get; set; }
- public Guid[] RemoteTrailerIds { get; set; }
-
- public override bool CanDownload()
- {
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- [IgnoreDataMember]
- public override bool SupportsThemeMedia
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return false; }
- }
-
- /// <summary>
- /// Gets or sets the remote trailers.
- /// </summary>
- /// <value>The remote trailers.</value>
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- [IgnoreDataMember]
- public override string MediaType
- {
- get { return Model.Entities.MediaType.Game; }
- }
-
- /// <summary>
- /// Gets or sets the players supported.
- /// </summary>
- /// <value>The players supported.</value>
- public int? PlayersSupported { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is place holder.
- /// </summary>
- /// <value><c>true</c> if this instance is place holder; otherwise, <c>false</c>.</value>
- public bool IsPlaceHolder { get; set; }
-
- /// <summary>
- /// Gets or sets the game system.
- /// </summary>
- /// <value>The game system.</value>
- public string GameSystem { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is multi part.
- /// </summary>
- /// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
- public bool IsMultiPart { get; set; }
-
- /// <summary>
- /// Holds the paths to the game files in the event this is a multipart game
- /// </summary>
- public string[] MultiPartGameFiles { get; set; }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
- var id = this.GetProviderId(MetadataProviders.Gamesdb);
-
- if (!string.IsNullOrEmpty(id))
- {
- list.Insert(0, "Game-Gamesdb-" + id);
- }
- return list;
- }
-
- public override IEnumerable<FileSystemMetadata> GetDeletePaths()
- {
- if (!IsInMixedFolder)
- {
- return new[] {
- new FileSystemMetadata
- {
- FullName = FileSystem.GetDirectoryName(Path),
- IsDirectory = true
- }
- };
- }
-
- return base.GetDeletePaths();
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Game;
- }
-
- public GameInfo GetLookupInfo()
- {
- var id = GetItemLookupInfo<GameInfo>();
-
- id.GameSystem = GameSystem;
-
- return id;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
deleted file mode 100644
index 6dc85f3e5..000000000
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class GameGenre : BaseItem, IItemByName
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
-
- public override string CreatePresentationUniqueKey()
- {
- return GetUserDataKeys()[0];
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- query.GenreIds = new[] { Id.ToString("N") };
- query.IncludeItemTypes = new[] { typeof(Game).Name };
-
- return LibraryManager.GetItemList(query);
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.GameGenrePath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
deleted file mode 100644
index c940b5953..000000000
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class GameSystem
- /// </summary>
- public class GameSystem : Folder, IHasLookupInfo<GameSystemInfo>
- {
- /// <summary>
- /// Return the id that should be used to key display prefs for this item.
- /// Default is based on the type for everything except actual generic folders.
- /// </summary>
- /// <value>The display prefs id.</value>
- [IgnoreDataMember]
- public override Guid DisplayPreferencesId
- {
- get
- {
- return Id;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 16;
- value /= 9;
-
- return value;
- }
-
- /// <summary>
- /// Gets or sets the game system.
- /// </summary>
- /// <value>The game system.</value>
- public string GameSystemName { get; set; }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- if (!string.IsNullOrEmpty(GameSystemName))
- {
- list.Insert(0, "GameSystem-" + GameSystemName);
- }
- return list;
- }
-
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- // Don't block. Determine by game
- return false;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Game;
- }
-
- public GameSystemInfo GetLookupInfo()
- {
- var id = GetItemLookupInfo<GameSystemInfo>();
-
- id.Path = Path;
-
- return id;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
deleted file mode 100644
index 569a0dfb8..000000000
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Controller.Entities.Audio;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Genre
- /// </summary>
- public class Genre : BaseItem, IItemByName
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
- public override string CreatePresentationUniqueKey()
- {
- return GetUserDataKeys()[0];
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsDisplayedAsFolder
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- query.GenreIds = new[] { Id.ToString("N") };
- query.ExcludeItemTypes = new[] { typeof(Game).Name, typeof(MusicVideo).Name, typeof(Audio.Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
-
- return LibraryManager.GetItemList(query);
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.GenrePath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs
deleted file mode 100644
index b70ad322d..000000000
--- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// This is just a marker interface to denote top level folders
- /// </summary>
- public interface ICollectionFolder
- {
- string CollectionType { get; }
- string Path { get; }
- string Name { get; }
- Guid Id { get; }
- string[] PhysicalLocations { get; }
- }
-
- public interface ISupportsUserSpecificView
- {
- bool EnableUserSpecificView { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs
deleted file mode 100644
index 5aecf4eac..000000000
--- a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IHasAspectRatio
- /// </summary>
- public interface IHasAspectRatio
- {
- /// <summary>
- /// Gets or sets the aspect ratio.
- /// </summary>
- /// <value>The aspect ratio.</value>
- string AspectRatio { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs b/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs
deleted file mode 100644
index 5e1ae2179..000000000
--- a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IHasDisplayOrder
- /// </summary>
- public interface IHasDisplayOrder
- {
- /// <summary>
- /// Gets or sets the display order.
- /// </summary>
- /// <value>The display order.</value>
- string DisplayOrder { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
deleted file mode 100644
index 54786134f..000000000
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasMediaSources : IHasMetadata
- {
- /// <summary>
- /// Gets the media sources.
- /// </summary>
- /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
- /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
- List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
- List<MediaStream> GetMediaStreams();
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
deleted file mode 100644
index b7d31b4d6..000000000
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IHasMetadata
- /// </summary>
- public interface IHasMetadata : IHasProviderIds, IHasUserData
- {
- /// <summary>
- /// Gets the preferred metadata country code.
- /// </summary>
- /// <returns>System.String.</returns>
- string GetPreferredMetadataCountryCode();
-
- /// <summary>
- /// Gets the date modified.
- /// </summary>
- /// <value>The date modified.</value>
- DateTime DateModified { get; set; }
-
- /// <summary>
- /// Gets or sets the date last saved.
- /// </summary>
- /// <value>The date last saved.</value>
- DateTime DateLastSaved { get; set; }
-
- SourceType SourceType { get; }
-
- /// <summary>
- /// Gets or sets the date last refreshed.
- /// </summary>
- /// <value>The date last refreshed.</value>
- DateTime DateLastRefreshed { get; set; }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool BeforeMetadataRefresh();
-
- /// <summary>
- /// Afters the metadata refresh.
- /// </summary>
- void AfterMetadataRefresh();
-
- /// <summary>
- /// Gets a value indicating whether [supports people].
- /// </summary>
- /// <value><c>true</c> if [supports people]; otherwise, <c>false</c>.</value>
- bool SupportsPeople { get; }
-
- bool RequiresRefresh();
-
- bool EnableRefreshOnDateModifiedChange { get; }
-
- string PresentationUniqueKey { get; set; }
-
- string GetPresentationUniqueKey();
- string CreatePresentationUniqueKey();
- bool StopRefreshIfLocalMetadataFound { get; }
-
- int? GetInheritedParentalRatingValue();
- int InheritedParentalRatingValue { get; set; }
- List<string> GetInheritedTags();
- 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>
- void 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>
- void 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; }
-
- ItemUpdateType OnMetadataChanged();
- }
-
- 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/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
deleted file mode 100644
index 90786d44d..000000000
--- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasProgramAttributes
- {
- bool IsMovie { get; set; }
- bool IsSports { get; set; }
- bool IsNews { get; set; }
- bool IsKids { get; set; }
- bool IsRepeat { get; set; }
- bool? IsHD { get; set; }
- bool IsSeries { get; set; }
- bool IsLive { get; set; }
- bool IsPremiere { get; set; }
- ProgramAudio? Audio { get; set; }
- string EpisodeTitle { get; set; }
- string ServiceName { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasScreenshots.cs b/MediaBrowser.Controller/Entities/IHasScreenshots.cs
deleted file mode 100644
index 2fd402bc2..000000000
--- a/MediaBrowser.Controller/Entities/IHasScreenshots.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IHasScreenshots
- /// </summary>
- public interface IHasScreenshots
- {
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
deleted file mode 100644
index 20efdc2b8..000000000
--- a/MediaBrowser.Controller/Entities/IHasSeries.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-using System;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasSeries
- {
- /// <summary>
- /// Gets the name of the series.
- /// </summary>
- /// <value>The name of the series.</value>
- string SeriesName { get; set; }
- string FindSeriesName();
- string FindSeriesSortName();
- Guid? SeriesId { get; set; }
- Guid? FindSeriesId();
- string SeriesPresentationUniqueKey { get; set; }
- string FindSeriesPresentationUniqueKey();
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
deleted file mode 100644
index f4905b7dc..000000000
--- a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasSpecialFeatures
- {
- /// <summary>
- /// Gets or sets the special feature ids.
- /// </summary>
- /// <value>The special feature ids.</value>
- Guid[] SpecialFeatureIds { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasStartDate.cs b/MediaBrowser.Controller/Entities/IHasStartDate.cs
deleted file mode 100644
index a6714fb96..000000000
--- a/MediaBrowser.Controller/Entities/IHasStartDate.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasStartDate
- {
- DateTime StartDate { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs
deleted file mode 100644
index 07dde3789..000000000
--- a/MediaBrowser.Controller/Entities/IHasTrailers.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasTrailers : IHasMetadata
- {
- /// <summary>
- /// Gets or sets the remote trailers.
- /// </summary>
- /// <value>The remote trailers.</value>
- MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets or sets the local trailer ids.
- /// </summary>
- /// <value>The local trailer ids.</value>
- Guid[] LocalTrailerIds { get; set; }
- Guid[] RemoteTrailerIds { get; set; }
- }
-
- public static class HasTrailerExtensions
- {
- /// <summary>
- /// Gets the trailer ids.
- /// </summary>
- /// <returns>List&lt;Guid&gt;.</returns>
- public static List<Guid> GetTrailerIds(this IHasTrailers item)
- {
- var list = item.LocalTrailerIds.ToList();
- list.AddRange(item.RemoteTrailerIds);
- return list;
- }
-
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs
deleted file mode 100644
index ab4f624e2..000000000
--- a/MediaBrowser.Controller/Entities/IHasUserData.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IHasUserData
- /// </summary>
- public interface IHasUserData
- {
- List<string> GetUserDataKeys();
-
- /// <summary>
- /// Fills the user data dto values.
- /// </summary>
- void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, ItemFields[] fields);
-
- bool EnableRememberingTrackSelections { get; }
-
- bool SupportsPlayedStatus { get; }
-
- Guid Id { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs b/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
deleted file mode 100644
index ba6311296..000000000
--- a/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHiddenFromDisplay
- {
- /// <summary>
- /// Determines whether the specified user is hidden.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns><c>true</c> if the specified user is hidden; otherwise, <c>false</c>.</returns>
- bool IsHiddenFromUser(User user);
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
deleted file mode 100644
index a02ca1c0c..000000000
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Marker interface
- /// </summary>
- public interface IItemByName : IHasMetadata
- {
- IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query);
- }
-
- public interface IHasDualAccess : IItemByName
- {
- bool IsAccessedByName { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IMetadataContainer.cs b/MediaBrowser.Controller/Entities/IMetadataContainer.cs
deleted file mode 100644
index 33aa08425..000000000
--- a/MediaBrowser.Controller/Entities/IMetadataContainer.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IMetadataContainer
- {
- /// <summary>
- /// Refreshes all metadata.
- /// </summary>
- /// <param name="refreshOptions">The refresh options.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs
deleted file mode 100644
index fbe5a06d0..000000000
--- a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Marker interface to denote a class that supports being hidden underneath it's boxset.
- /// Just about anything can be placed into a boxset,
- /// but movies should also only appear underneath and not outside separately (subject to configuration).
- /// </summary>
- public interface ISupportsBoxSetGrouping
- {
- }
-}
diff --git a/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs b/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs
deleted file mode 100644
index 2507c8ee6..000000000
--- a/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface ISupportsPlaceHolders
- {
- /// <summary>
- /// Gets a value indicating whether this instance is place holder.
- /// </summary>
- /// <value><c>true</c> if this instance is place holder; otherwise, <c>false</c>.</value>
- bool IsPlaceHolder { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IVirtualFolderCreator.cs b/MediaBrowser.Controller/Entities/IVirtualFolderCreator.cs
deleted file mode 100644
index 57e9e8d5d..000000000
--- a/MediaBrowser.Controller/Entities/IVirtualFolderCreator.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Interface IVirtualFolderCreator
- /// </summary>
- public interface IVirtualFolderCreator
- {
- /// <summary>
- /// Gets the folder.
- /// </summary>
- /// <returns>Folder.</returns>
- BasePluginFolder GetFolder();
- }
-}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
deleted file mode 100644
index a7f6c6014..000000000
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ /dev/null
@@ -1,243 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Configuration;
-using System.Linq;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class InternalItemsQuery
- {
- public bool Recursive { get; set; }
-
- public int? StartIndex { get; set; }
-
- public int? Limit { get; set; }
-
- public User User { get; set; }
-
- public BaseItem SimilarTo { get; set; }
-
- public bool? IsFolder { get; set; }
- public bool? IsFavorite { get; set; }
- public bool? IsFavoriteOrLiked { get; set; }
- public bool? IsLiked { get; set; }
- public bool? IsPlayed { get; set; }
- public bool? IsResumable { get; set; }
- public bool? IncludeItemsByName { get; set; }
-
- public string[] MediaTypes { get; set; }
- public string[] IncludeItemTypes { get; set; }
- public string[] ExcludeItemTypes { get; set; }
- public string[] ExcludeTags { get; set; }
- public string[] ExcludeInheritedTags { get; set; }
- public string[] Genres { get; set; }
-
- public bool? IsSpecialSeason { get; set; }
- public bool? IsMissing { get; set; }
- public bool? IsUnaired { get; set; }
- public bool? CollapseBoxSetItems { get; set; }
-
- public string NameStartsWithOrGreater { get; set; }
- public string NameStartsWith { get; set; }
- public string NameLessThan { get; set; }
- public string NameContains { get; set; }
- public string MinSortName { get; set; }
-
- public string PresentationUniqueKey { get; set; }
- public string Path { get; set; }
- public string PathNotStartsWith { get; set; }
- public string Name { get; set; }
-
- public string Person { get; set; }
- public string[] PersonIds { get; set; }
- public string[] ItemIds { get; set; }
- public string[] ExcludeItemIds { get; set; }
- public string AdjacentTo { get; set; }
- public string[] PersonTypes { get; set; }
-
- public bool? Is3D { get; set; }
- public bool? IsHD { get; set; }
- public bool? IsInBoxSet { get; set; }
- public bool? IsLocked { get; set; }
- public bool? IsPlaceHolder { get; set; }
-
- public bool? HasImdbId { get; set; }
- public bool? HasOverview { get; set; }
- public bool? HasTmdbId { get; set; }
- public bool? HasOfficialRating { get; set; }
- public bool? HasTvdbId { get; set; }
- public bool? HasThemeSong { get; set; }
- public bool? HasThemeVideo { get; set; }
- public bool? HasSubtitles { get; set; }
- public bool? HasSpecialFeature { get; set; }
- public bool? HasTrailer { get; set; }
- public bool? HasParentalRating { get; set; }
-
- public string[] StudioIds { get; set; }
- public string[] GenreIds { get; set; }
- public ImageType[] ImageTypes { get; set; }
- public VideoType[] VideoTypes { get; set; }
- public UnratedItem[] BlockUnratedItems { get; set; }
- public int[] Years { get; set; }
- public string[] Tags { get; set; }
- public string[] OfficialRatings { get; set; }
-
- public DateTime? MinPremiereDate { get; set; }
- public DateTime? MaxPremiereDate { get; set; }
- public DateTime? MinStartDate { get; set; }
- public DateTime? MaxStartDate { get; set; }
- public DateTime? MinEndDate { get; set; }
- public DateTime? MaxEndDate { get; set; }
- public bool? IsAiring { get; set; }
-
- public bool? IsMovie { get; set; }
- public bool? IsSports { get; set; }
- public bool? IsKids { get; set; }
- public bool? IsNews { get; set; }
- public bool? IsSeries { get; set; }
-
- public int? MinPlayers { get; set; }
- public int? MaxPlayers { get; set; }
- public int? MinIndexNumber { get; set; }
- public int? AiredDuringSeason { get; set; }
- public double? MinCriticRating { get; set; }
- public double? MinCommunityRating { get; set; }
-
- public string[] ChannelIds { get; set; }
-
- internal List<Guid> ItemIdsFromPersonFilters { get; set; }
- public int? ParentIndexNumber { get; set; }
- public int? ParentIndexNumberNotEquals { get; set; }
- public int? IndexNumber { get; set; }
- public int? MinParentalRating { get; set; }
- public int? MaxParentalRating { get; set; }
-
- public bool? HasDeadParentId { get; set; }
- public bool? IsVirtualItem { get; set; }
-
- public Guid? ParentId { get; set; }
- public string ParentType { get; set; }
- public string[] AncestorIds { get; set; }
- public string[] TopParentIds { get; set; }
-
- public BaseItem Parent
- {
- set
- {
- if (value == null)
- {
- ParentId = null;
- ParentType = null;
- }
- else
- {
- ParentId = value.Id;
- ParentType = value.GetType().Name;
- }
- }
- }
-
- public string[] PresetViews { get; set; }
- public TrailerType[] TrailerTypes { get; set; }
- public SourceType[] SourceTypes { get; set; }
-
- public SeriesStatus[] SeriesStatuses { get; set; }
- public string ExternalSeriesId { get; set; }
- public string ExternalId { get; set; }
-
- public string[] AlbumIds { get; set; }
- public string[] ArtistIds { get; set; }
- public string[] ExcludeArtistIds { get; set; }
- public string AncestorWithPresentationUniqueKey { get; set; }
- 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; }
- public bool EnableGroupByMetadataKey { get; set; }
- public bool? HasChapterImages { get; set; }
-
- public Tuple<string, SortOrder>[] OrderBy { get; set; }
-
- public DateTime? MinDateCreated { get; set; }
- public DateTime? MinDateLastSaved { get; set; }
- public DateTime? MinDateLastSavedForUser { get; set; }
-
- public DtoOptions DtoOptions { get; set; }
- public int MinSimilarityScore { get; set; }
- public string HasNoAudioTrackWithLanguage { get; set; }
- public string HasNoInternalSubtitleTrackWithLanguage { get; set; }
- public string HasNoExternalSubtitleTrackWithLanguage { get; set; }
- public string HasNoSubtitleTrackWithLanguage { get; set; }
-
- public InternalItemsQuery()
- {
- MinSimilarityScore = 20;
-
- GroupByPresentationUniqueKey = true;
- EnableTotalRecordCount = true;
-
- DtoOptions = new DtoOptions();
- AlbumIds = new string[] { };
- ArtistIds = new string[] { };
- ExcludeArtistIds = new string[] { };
- ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- BlockUnratedItems = new UnratedItem[] { };
- Tags = new string[] { };
- OfficialRatings = new string[] { };
- MediaTypes = new string[] { };
- IncludeItemTypes = new string[] { };
- ExcludeItemTypes = new string[] { };
- Genres = new string[] { };
- StudioIds = new string[] { };
- GenreIds = new string[] { };
- ImageTypes = new ImageType[] { };
- VideoTypes = new VideoType[] { };
- Years = new int[] { };
- PersonTypes = new string[] { };
- PersonIds = new string[] { };
- ChannelIds = new string[] { };
- ItemIds = new string[] { };
- ExcludeItemIds = new string[] { };
- AncestorIds = new string[] { };
- TopParentIds = new string[] { };
- ExcludeTags = new string[] { };
- ExcludeInheritedTags = new string[] { };
- PresetViews = new string[] { };
- TrailerTypes = new TrailerType[] { };
- SourceTypes = new SourceType[] { };
- SeriesStatuses = new SeriesStatus[] { };
- OrderBy = new Tuple<string, SortOrder>[] { };
- }
-
- public InternalItemsQuery(User user)
- : this()
- {
- SetUser(user);
- }
-
- public void SetUser(User user)
- {
- if (user != null)
- {
- var policy = user.Policy;
- MaxParentalRating = policy.MaxParentalRating;
-
- if (policy.MaxParentalRating.HasValue)
- {
- BlockUnratedItems = policy.BlockUnratedItems.Where(i => i != UnratedItem.Other).ToArray();
- }
-
- ExcludeInheritedTags = policy.BlockedTags;
-
- User = user;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
deleted file mode 100644
index 9e0e9c208..000000000
--- a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class InternalPeopleQuery
- {
- public Guid ItemId { get; set; }
- public string[] PersonTypes { get; set; }
- public List<string> ExcludePersonTypes { get; set; }
- public int? MaxListOrder { get; set; }
- public Guid AppearsInItemId { get; set; }
- public string NameContains { get; set; }
-
- public InternalPeopleQuery()
- {
- PersonTypes = new string[] {};
- ExcludePersonTypes = new List<string>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
deleted file mode 100644
index bd0011c4b..000000000
--- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class ItemImageInfo
- {
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public ImageType Type { get; set; }
-
- /// <summary>
- /// Gets or sets the date modified.
- /// </summary>
- /// <value>The date modified.</value>
- public DateTime DateModified { get; set; }
-
- public int Width { get; set; }
- public int Height { get; set; }
-
- [IgnoreDataMember]
- public bool IsLocalFile
- {
- get
- {
- if (Path != null)
- {
- if (Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
- return true;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs
deleted file mode 100644
index 6031a2448..000000000
--- a/MediaBrowser.Controller/Entities/LinkedChild.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class LinkedChild
- {
- public string Path { get; set; }
- public LinkedChildType Type { get; set; }
-
- [IgnoreDataMember]
- public string Id { get; set; }
-
- /// <summary>
- /// Serves as a cache
- /// </summary>
- public Guid? ItemId { get; set; }
-
- public static LinkedChild Create(BaseItem item)
- {
- return new LinkedChild
- {
- Path = item.Path,
- Type = LinkedChildType.Manual
- };
- }
-
- public LinkedChild()
- {
- Id = Guid.NewGuid().ToString("N");
- }
- }
-
- public enum LinkedChildType
- {
- Manual = 0,
- Shortcut = 1
- }
-
- public class LinkedChildComparer : IEqualityComparer<LinkedChild>
- {
- private readonly IFileSystem _fileSystem;
-
- public LinkedChildComparer(IFileSystem fileSystem)
- {
- _fileSystem = fileSystem;
- }
-
- public bool Equals(LinkedChild x, LinkedChild y)
- {
- if (x.Type == y.Type)
- {
- return _fileSystem.AreEqual(x.Path, y.Path);
- }
- return false;
- }
-
- public int GetHashCode(LinkedChild obj)
- {
- return (obj.Path + obj.Type).GetHashCode();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
deleted file mode 100644
index 268860ff0..000000000
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ /dev/null
@@ -1,216 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.Movies
-{
- /// <summary>
- /// Class BoxSet
- /// </summary>
- public class BoxSet : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares
- {
- public List<Share> Shares { get; set; }
-
- public BoxSet()
- {
- RemoteTrailers = EmptyMediaUrlArray;
- LocalTrailerIds = EmptyGuidArray;
- RemoteTrailerIds = EmptyGuidArray;
-
- DisplayOrder = ItemSortBy.PremiereDate;
- Shares = new List<Share>();
- }
-
- [IgnoreDataMember]
- protected override bool FilterLinkedChildrenPerUser
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return true; }
- }
-
- public Guid[] LocalTrailerIds { get; set; }
- public Guid[] RemoteTrailerIds { get; set; }
-
- /// <summary>
- /// Gets or sets the remote trailers.
- /// </summary>
- /// <value>The remote trailers.</value>
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets or sets the display order.
- /// </summary>
- /// <value>The display order.</value>
- public string DisplayOrder { get; set; }
-
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- return config.BlockUnratedItems.Contains(UnratedItem.Movie);
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Movie;
- }
-
- protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- if (IsLegacyBoxSet)
- {
- return base.GetNonCachedChildren(directoryService);
- }
- return new List<BaseItem>();
- }
-
- protected override List<BaseItem> LoadChildren()
- {
- if (IsLegacyBoxSet)
- {
- return base.LoadChildren();
- }
-
- // Save a trip to the database
- return new List<BaseItem>();
- }
-
- [IgnoreDataMember]
- private bool IsLegacyBoxSet
- {
- get
- {
- if (string.IsNullOrWhiteSpace(Path))
- {
- return false;
- }
-
- if (LinkedChildren.Length > 0)
- {
- return false;
- }
-
- return !FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, Path);
- }
- }
-
- [IgnoreDataMember]
- public override bool IsPreSorted
- {
- get
- {
- return true;
- }
- }
-
- public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
- {
- return true;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- /// <summary>
- /// Updates the official rating based on content and returns true or false indicating if it changed.
- /// </summary>
- /// <returns></returns>
- public bool UpdateRatingToContent()
- {
- var currentOfficialRating = OfficialRating;
-
- // Gather all possible ratings
- var ratings = GetLinkedChildren()
- .Select(i => i.OfficialRating)
- .Where(i => !string.IsNullOrEmpty(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => new Tuple<string, int?>(i, LocalizationManager.GetRatingLevel(i)))
- .OrderBy(i => i.Item2 ?? 1000)
- .Select(i => i.Item1);
-
- OfficialRating = ratings.FirstOrDefault() ?? currentOfficialRating;
-
- return !string.Equals(currentOfficialRating ?? string.Empty, OfficialRating ?? string.Empty,
- StringComparison.OrdinalIgnoreCase);
- }
-
- public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- var children = base.GetChildren(user, includeLinkedChildren);
-
- if (string.Equals(DisplayOrder, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase))
- {
- // Sort by name
- return LibraryManager.Sort(children, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- }
-
- if (string.Equals(DisplayOrder, ItemSortBy.PremiereDate, StringComparison.OrdinalIgnoreCase))
- {
- // Sort by release date
- return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- }
-
- // Default sorting
- return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- }
-
- public BoxSetInfo GetLookupInfo()
- {
- return GetItemLookupInfo<BoxSetInfo>();
- }
-
- public override bool IsVisible(User user)
- {
- var userId = user.Id.ToString("N");
-
- // Need to check Count > 0 for boxsets created prior to the introduction of Shares
- if (Shares.Count > 0 && Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)))
- {
- return true;
- }
-
- if (base.IsVisible(user))
- {
- return base.GetChildren(user, true).Count > 0;
- }
-
- return false;
- }
-
- public override bool IsVisibleStandalone(User user)
- {
- return IsVisible(user);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
deleted file mode 100644
index 2e0e01944..000000000
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.Movies
-{
- /// <summary>
- /// Class Movie
- /// </summary>
- public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
- {
- public Guid[] SpecialFeatureIds { get; set; }
-
- public Movie()
- {
- SpecialFeatureIds = EmptyGuidArray;
- RemoteTrailers = EmptyMediaUrlArray;
- LocalTrailerIds = EmptyGuidArray;
- RemoteTrailerIds = EmptyGuidArray;
- }
-
- public Guid[] LocalTrailerIds { get; set; }
- public Guid[] RemoteTrailerIds { get; set; }
-
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the TMDB collection.
- /// </summary>
- /// <value>The name of the TMDB collection.</value>
- public string TmdbCollectionName { get; set; }
-
- [IgnoreDataMember]
- public string CollectionName
- {
- get { return TmdbCollectionName; }
- set { TmdbCollectionName = value; }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- 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 && !IsInMixedFolder)
- {
- var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
-
- if (specialFeaturesChanged)
- {
- hasChanges = true;
- }
- }
-
- return hasChanges;
- }
-
- 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).ToArray();
-
- var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
-
- var ownerId = Id;
-
- var tasks = newItems.Select(i =>
- {
- var subOptions = new MetadataRefreshOptions(options);
-
- if (i.OwnerId != ownerId)
- {
- i.OwnerId = ownerId;
- subOptions.ForceSave = true;
- }
-
- return RefreshMetadataForOwnedItem(i, false, subOptions, cancellationToken);
- });
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
- SpecialFeatureIds = newItemIds;
-
- return itemsChanged;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Movie;
- }
-
- public MovieInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<MovieInfo>();
-
- if (!IsInMixedFolder)
- {
- var name = System.IO.Path.GetFileName(ContainingFolderPath);
-
- if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso)
- {
- if (string.Equals(name, System.IO.Path.GetFileName(Path), StringComparison.OrdinalIgnoreCase))
- {
- // if the folder has the file extension, strip it
- name = System.IO.Path.GetFileNameWithoutExtension(name);
- }
- }
-
- info.Name = name;
- }
-
- return info;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (!ProductionYear.HasValue)
- {
- var info = LibraryManager.ParseName(Name);
-
- var yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- else
- {
- // Try to get the year from the folder name
- if (!IsInMixedFolder)
- {
- info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
-
- yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- }
- }
- }
-
- return hasChanges;
- }
-
- public override List<ExternalUrl> GetRelatedUrls()
- {
- var list = base.GetRelatedUrls();
-
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(imdbId))
- {
- list.Add(new ExternalUrl
- {
- Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
- });
- }
-
- return list;
- }
-
- [IgnoreDataMember]
- public override bool StopRefreshIfLocalMetadataFound
- {
- get
- {
- // Need people id's from internet metadata
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
deleted file mode 100644
index b7470d679..000000000
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasLookupInfo<MusicVideoInfo>
- {
- [IgnoreDataMember]
- public string[] Artists { get; set; }
-
- public MusicVideo()
- {
- Artists = EmptyStringArray;
- }
-
- [IgnoreDataMember]
- public string[] AllArtists
- {
- get
- {
- return Artists;
- }
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Music;
- }
-
- public MusicVideoInfo GetLookupInfo()
- {
- return GetItemLookupInfo<MusicVideoInfo>();
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (!ProductionYear.HasValue)
- {
- var info = LibraryManager.ParseName(Name);
-
- var yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- else
- {
- // Try to get the year from the folder name
- if (!IsInMixedFolder)
- {
- info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
-
- yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- }
- }
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs
deleted file mode 100644
index 412eb9499..000000000
--- a/MediaBrowser.Controller/Entities/PeopleHelper.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
- public static class PeopleHelper
- {
- public static void AddPerson(List<PersonInfo> people, PersonInfo person)
- {
- if (person == null)
- {
- throw new ArgumentNullException("person");
- }
-
- if (string.IsNullOrWhiteSpace(person.Name))
- {
- throw new ArgumentNullException();
- }
-
- // Normalize
- if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
- {
- person.Type = PersonType.GuestStar;
- }
- else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase))
- {
- person.Type = PersonType.Director;
- }
- else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase))
- {
- person.Type = PersonType.Producer;
- }
- else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase))
- {
- person.Type = PersonType.Writer;
- }
-
- // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes
- if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
- {
- var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase));
-
- if (existing != null)
- {
- existing.Type = PersonType.GuestStar;
- MergeExisting(existing, person);
- return;
- }
- }
-
- if (string.Equals(person.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase))
- {
- // If the actor already exists without a role and we have one, fill it in
- var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase) || p.Type.Equals(PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)));
- if (existing == null)
- {
- // Wasn't there - add it
- people.Add(person);
- }
- else
- {
- // Was there, if no role and we have one - fill it in
- if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role))
- {
- existing.Role = person.Role;
- }
-
- MergeExisting(existing, person);
- }
- }
- else
- {
- var existing = people.FirstOrDefault(p =>
- string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) &&
- string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase));
-
- // Check for dupes based on the combination of Name and Type
- if (existing == null)
- {
- people.Add(person);
- }
- else
- {
- MergeExisting(existing, person);
- }
- }
- }
-
- private static void MergeExisting(PersonInfo existing, PersonInfo person)
- {
- existing.SortOrder = person.SortOrder ?? existing.SortOrder;
- existing.ImageUrl = person.ImageUrl ?? existing.ImageUrl;
-
- foreach (var id in person.ProviderIds)
- {
- existing.SetProviderId(id.Key, id.Value);
- }
- }
-
- public static bool ContainsPerson(List<PersonInfo> people, string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- foreach (var i in people)
- {
- if (string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
deleted file mode 100644
index b3a91f944..000000000
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ /dev/null
@@ -1,229 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// This is the full Person object that can be retrieved with all of it's data.
- /// </summary>
- public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
- public override string CreatePresentationUniqueKey()
- {
- return GetUserDataKeys()[0];
- }
-
- public PersonLookupInfo GetLookupInfo()
- {
- return GetItemLookupInfo<PersonLookupInfo>();
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- query.PersonIds = new[] { Id.ToString("N") };
-
- return LibraryManager.GetItemList(query);
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- [IgnoreDataMember]
- public override bool EnableAlphaNumericSorting
- {
- get
- {
- return false;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validFilename = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- string subFolderPrefix = null;
-
- foreach (char c in validFilename)
- {
- if (char.IsLetterOrDigit(c))
- {
- subFolderPrefix = c.ToString();
- break;
- }
- }
-
- var path = ConfigurationManager.ApplicationPaths.PeoplePath;
-
- return string.IsNullOrEmpty(subFolderPrefix) ?
- System.IO.Path.Combine(path, validFilename) :
- System.IO.Path.Combine(path, subFolderPrefix, validFilename);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-
- /// <summary>
- /// This is the small Person stub that is attached to BaseItems
- /// </summary>
- public class PersonInfo : IHasProviderIds
- {
- public PersonInfo()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
- public Guid ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the role.
- /// </summary>
- /// <value>The role.</value>
- public string Role { get; set; }
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the sort order - ascending
- /// </summary>
- /// <value>The sort order.</value>
- public int? SortOrder { get; set; }
-
- public string ImageUrl { get; set; }
-
- public Dictionary<string, string> ProviderIds { get; set; }
-
- /// <summary>
- /// Returns a <see cref="System.String" /> that represents this instance.
- /// </summary>
- /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
- public override string ToString()
- {
- return Name;
- }
-
- public bool IsType(string type)
- {
- return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
deleted file mode 100644
index 11db633ba..000000000
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class Photo : BaseItem
- {
- [IgnoreDataMember]
- public override bool SupportsLocalMetadata
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Photo;
- }
- }
-
- [IgnoreDataMember]
- public override Folder LatestItemsIndexContainer
- {
- get
- {
- return AlbumEntity;
- }
- }
-
-
- [IgnoreDataMember]
- public PhotoAlbum AlbumEntity
- {
- get
- {
- var parents = GetParents();
- foreach (var parent in parents)
- {
- var photoAlbum = parent as PhotoAlbum;
- if (photoAlbum != null)
- {
- return photoAlbum;
- }
- }
- return null;
- }
- }
-
- [IgnoreDataMember]
- public override bool EnableRefreshOnDateModifiedChange
- {
- get { return true; }
- }
-
- public override bool CanDownload()
- {
- return true;
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- if (Width.HasValue && Height.HasValue)
- {
- double width = Width.Value;
- double height = Height.Value;
-
- if (Orientation.HasValue)
- {
- switch (Orientation.Value)
- {
- case ImageOrientation.LeftBottom:
- case ImageOrientation.LeftTop:
- case ImageOrientation.RightBottom:
- case ImageOrientation.RightTop:
- var temp = height;
- height = width;
- width = temp;
- break;
- }
- }
-
- width /= Height.Value;
- return width;
- }
-
- return base.GetDefaultPrimaryImageAspectRatio();
- }
-
- public int? Width { get; set; }
- public int? Height { get; set; }
- public string CameraMake { get; set; }
- public string CameraModel { get; set; }
- public string Software { get; set; }
- public double? ExposureTime { get; set; }
- public double? FocalLength { get; set; }
- public ImageOrientation? Orientation { get; set; }
- public double? Aperture { get; set; }
- public double? ShutterSpeed { get; set; }
-
- public double? Latitude { get; set; }
- public double? Longitude { get; set; }
- public double? Altitude { get; set; }
- public int? IsoSpeedRating { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
deleted file mode 100644
index af9d8c801..000000000
--- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class PhotoAlbum : Folder
- {
- [IgnoreDataMember]
- public override bool AlwaysScanInternalMetadataPath
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs
deleted file mode 100644
index e194f6238..000000000
--- a/MediaBrowser.Controller/Entities/Share.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasShares
- {
- List<Share> Shares { get; set; }
- }
-
- public class Share
- {
- public string UserId { get; set; }
- public bool CanEdit { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/SourceType.cs b/MediaBrowser.Controller/Entities/SourceType.cs
deleted file mode 100644
index 9c307b4e6..000000000
--- a/MediaBrowser.Controller/Entities/SourceType.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- public enum SourceType
- {
- Library = 0,
- Channel = 1,
- LiveTV = 2
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
deleted file mode 100644
index a6a72d994..000000000
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Studio
- /// </summary>
- public class Studio : BaseItem, IItemByName
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
- return list;
- }
- public override string CreatePresentationUniqueKey()
- {
- return GetUserDataKeys()[0];
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsDisplayedAsFolder
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 16;
- value /= 9;
-
- return value;
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- query.StudioIds = new[] { Id.ToString("N") };
-
- return LibraryManager.GetItemList(query);
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.StudioPath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
deleted file mode 100644
index 3f52dfc93..000000000
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ /dev/null
@@ -1,385 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.TV
-{
- /// <summary>
- /// Class Episode
- /// </summary>
- public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries
- {
- public Episode()
- {
- RemoteTrailers = EmptyMediaUrlArray;
- LocalTrailerIds = EmptyGuidArray;
- RemoteTrailerIds = EmptyGuidArray;
- }
-
- public Guid[] LocalTrailerIds { get; set; }
- public Guid[] RemoteTrailerIds { get; set; }
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets the season in which it aired.
- /// </summary>
- /// <value>The aired season.</value>
- public int? AirsBeforeSeasonNumber { get; set; }
- public int? AirsAfterSeasonNumber { get; set; }
- public int? AirsBeforeEpisodeNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the DVD season number.
- /// </summary>
- /// <value>The DVD season number.</value>
- public int? DvdSeasonNumber { get; set; }
- /// <summary>
- /// Gets or sets the DVD episode number.
- /// </summary>
- /// <value>The DVD episode number.</value>
- public float? DvdEpisodeNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the absolute episode number.
- /// </summary>
- /// <value>The absolute episode number.</value>
- public int? AbsoluteEpisodeNumber { get; set; }
-
- /// <summary>
- /// This is the ending episode number for double episodes.
- /// </summary>
- /// <value>The index number.</value>
- public int? IndexNumberEnd { get; set; }
-
- public string FindSeriesSortName()
- {
- var series = Series;
- return series == null ? SeriesName : series.SortName;
- }
-
- [IgnoreDataMember]
- protected override bool SupportsOwnedItems
- {
- get
- {
- return IsStacked || MediaSourceCount > 1;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public int? AiredSeasonNumber
- {
- get
- {
- return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber;
- }
- }
-
- [IgnoreDataMember]
- public override Folder LatestItemsIndexContainer
- {
- get
- {
- return Series;
- }
- }
-
- [IgnoreDataMember]
- public override Guid? DisplayParentId
- {
- get
- {
- return SeasonId;
- }
- }
-
- [IgnoreDataMember]
- protected override bool EnableDefaultVideoUserDataKeys
- {
- get
- {
- return false;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 16;
- value /= 9;
-
- return value;
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var series = Series;
- if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
- {
- var seriesUserDataKeys = series.GetUserDataKeys();
- var take = seriesUserDataKeys.Count;
- if (seriesUserDataKeys.Count > 1)
- {
- take--;
- }
- list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
- }
-
- return list;
- }
-
- /// <summary>
- /// This Episode's Series Instance
- /// </summary>
- /// <value>The series.</value>
- [IgnoreDataMember]
- public Series Series
- {
- get
- {
- var seriesId = SeriesId ?? FindSeriesId();
- return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
- }
- }
-
- [IgnoreDataMember]
- public Season Season
- {
- get
- {
- var seasonId = SeasonId ?? FindSeasonId();
- return seasonId.HasValue ? (LibraryManager.GetItemById(seasonId.Value) as Season) : null;
- }
- }
-
- [IgnoreDataMember]
- public bool IsInSeasonFolder
- {
- get
- {
- return FindParent<Season>() != null;
- }
- }
-
- [IgnoreDataMember]
- public string SeriesPresentationUniqueKey { get; set; }
-
- [IgnoreDataMember]
- public string SeriesName { get; set; }
-
- [IgnoreDataMember]
- public string SeasonName { get; set; }
-
- public string FindSeriesPresentationUniqueKey()
- {
- var series = Series;
- return series == null ? null : series.PresentationUniqueKey;
- }
-
- public string FindSeasonName()
- {
- var season = Season;
-
- if (season == null)
- {
- if (ParentIndexNumber.HasValue)
- {
- return "Season " + ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture);
- }
- return "Season Unknown";
- }
-
- return season.Name;
- }
-
- public string FindSeriesName()
- {
- var series = Series;
- return series == null ? SeriesName : series.Name;
- }
-
- public Guid? FindSeasonId()
- {
- var season = FindParent<Season>();
-
- // Episodes directly in series folder
- if (season == null)
- {
- var series = Series;
-
- if (series != null && ParentIndexNumber.HasValue)
- {
- var findNumber = ParentIndexNumber.Value;
-
- season = series.Children
- .OfType<Season>()
- .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
- }
- }
-
- return season == null ? (Guid?)null : season.Id;
- }
-
- /// <summary>
- /// Creates the name of the sort.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateSortName()
- {
- return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ") : "")
- + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
- }
-
- /// <summary>
- /// Determines whether [contains episode number] [the specified number].
- /// </summary>
- /// <param name="number">The number.</param>
- /// <returns><c>true</c> if [contains episode number] [the specified number]; otherwise, <c>false</c>.</returns>
- public bool ContainsEpisodeNumber(int number)
- {
- if (IndexNumber.HasValue)
- {
- if (IndexNumberEnd.HasValue)
- {
- return number >= IndexNumber.Value && number <= IndexNumberEnd.Value;
- }
-
- return IndexNumber.Value == number;
- }
-
- return false;
- }
-
- [IgnoreDataMember]
- public override bool SupportsRemoteImageDownloading
- {
- get
- {
- if (IsMissingEpisode)
- {
- return false;
- }
-
- return true;
- }
- }
-
- [IgnoreDataMember]
- public bool IsMissingEpisode
- {
- get
- {
- return LocationType == LocationType.Virtual;
- }
- }
-
- [IgnoreDataMember]
- public Guid? SeasonId { get; set; }
- [IgnoreDataMember]
- public Guid? SeriesId { get; set; }
-
- public Guid? FindSeriesId()
- {
- var series = FindParent<Series>();
- return series == null ? (Guid?)null : series.Id;
- }
-
- public override IEnumerable<Guid> GetAncestorIds()
- {
- var list = base.GetAncestorIds().ToList();
-
- var seasonId = SeasonId;
-
- if (seasonId.HasValue && !list.Contains(seasonId.Value))
- {
- list.Add(seasonId.Value);
- }
-
- return list;
- }
-
- public override IEnumerable<FileSystemMetadata> GetDeletePaths()
- {
- return new[] {
- new FileSystemMetadata
- {
- FullName = Path,
- IsDirectory = IsFolder
- }
- }.Concat(GetLocalMetadataFilesToDelete());
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Series;
- }
-
- public EpisodeInfo GetLookupInfo()
- {
- var id = GetItemLookupInfo<EpisodeInfo>();
-
- var series = Series;
-
- if (series != null)
- {
- id.SeriesProviderIds = series.ProviderIds;
- }
-
- id.IsMissingEpisode = IsMissingEpisode;
- id.IndexNumberEnd = IndexNumberEnd;
-
- return id;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- try
- {
- if (LibraryManager.FillMissingEpisodeNumbersFromPath(this))
- {
- hasChanges = true;
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString());
- }
-
- if (!ParentIndexNumber.HasValue)
- {
- var season = Season;
- if (season != null)
- {
- if (season.ParentIndexNumber.HasValue)
- {
- ParentIndexNumber = season.ParentIndexNumber;
- hasChanges = true;
- }
- }
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
deleted file mode 100644
index 00bb75fa7..000000000
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-using System;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.TV
-{
- /// <summary>
- /// Class Season
- /// </summary>
- public class Season : Folder, IHasSeries, IHasLookupInfo<SeasonInfo>
- {
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool IsPreSorted
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsDateLastMediaAdded
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override Guid? DisplayParentId
- {
- get { return SeriesId; }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- public string FindSeriesSortName()
- {
- var series = Series;
- return series == null ? SeriesName : series.SortName;
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var series = Series;
- if (series != null)
- {
- list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000")));
- }
-
- return list;
- }
-
- public override int GetChildCount(User user)
- {
- var result = GetChildren(user, true).Count;
-
- return result;
- }
-
- /// <summary>
- /// This Episode's Series Instance
- /// </summary>
- /// <value>The series.</value>
- [IgnoreDataMember]
- public Series Series
- {
- get
- {
- var seriesId = SeriesId ?? FindSeriesId();
- return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
- }
- }
-
- [IgnoreDataMember]
- public string SeriesPath
- {
- get
- {
- var series = Series;
-
- if (series != null)
- {
- return series.Path;
- }
-
- return FileSystem.GetDirectoryName(Path);
- }
- }
-
- public override string CreatePresentationUniqueKey()
- {
- if (IndexNumber.HasValue)
- {
- var series = Series;
- if (series != null)
- {
- return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000");
- }
- }
-
- return base.CreatePresentationUniqueKey();
- }
-
- /// <summary>
- /// Creates the name of the sort.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateSortName()
- {
- return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
- }
-
- protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
- {
- if (query.User == null)
- {
- return base.GetItemsInternal(query);
- }
-
- var user = query.User;
-
- Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
-
- var items = GetEpisodes(user, query.DtoOptions).Where(filter);
-
- var result = PostFilterAndSort(items, query, false, false);
-
- return result;
- }
-
- /// <summary>
- /// Gets the episodes.
- /// </summary>
- public List<BaseItem> GetEpisodes(User user, DtoOptions options)
- {
- return GetEpisodes(Series, user, options);
- }
-
- public List<BaseItem> GetEpisodes(Series series, User user, DtoOptions options)
- {
- return GetEpisodes(series, user, null, options);
- }
-
- public List<BaseItem> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes, DtoOptions options)
- {
- return series.GetSeasonEpisodes(this, user, allSeriesEpisodes, options);
- }
-
- public List<BaseItem> GetEpisodes()
- {
- return Series.GetSeasonEpisodes(this, null, null, new DtoOptions(true));
- }
-
- public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- return GetEpisodes(user, new DtoOptions(true));
- }
-
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- // Don't block. Let either the entire series rating or episode rating determine it
- return false;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Series;
- }
-
- [IgnoreDataMember]
- public string SeriesPresentationUniqueKey { get; set; }
-
- [IgnoreDataMember]
- public string SeriesName { get; set; }
-
- [IgnoreDataMember]
- public Guid? SeriesId { get; set; }
-
- public string FindSeriesPresentationUniqueKey()
- {
- var series = Series;
- return series == null ? null : series.PresentationUniqueKey;
- }
-
- public string FindSeriesName()
- {
- var series = Series;
- return series == null ? SeriesName : series.Name;
- }
-
- public Guid? FindSeriesId()
- {
- var series = FindParent<Series>();
- return series == null ? (Guid?)null : series.Id;
- }
-
- /// <summary>
- /// Gets the lookup information.
- /// </summary>
- /// <returns>SeasonInfo.</returns>
- public SeasonInfo GetLookupInfo()
- {
- var id = GetItemLookupInfo<SeasonInfo>();
-
- var series = Series;
-
- if (series != null)
- {
- id.SeriesProviderIds = series.ProviderIds;
- }
-
- return id;
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
- {
- IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path);
-
- // If a change was made record it
- if (IndexNumber.HasValue)
- {
- hasChanges = true;
- }
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
deleted file mode 100644
index ccd0a7636..000000000
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ /dev/null
@@ -1,559 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities.TV
-{
- /// <summary>
- /// Class Series
- /// </summary>
- public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer
- {
- public Series()
- {
- RemoteTrailers = EmptyMediaUrlArray;
- LocalTrailerIds = EmptyGuidArray;
- RemoteTrailerIds = EmptyGuidArray;
- AirDays = new DayOfWeek[] { };
- }
-
- public DayOfWeek[] AirDays { get; set; }
- public string AirTime { get; set; }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool IsPreSorted
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsDateLastMediaAdded
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return true; }
- }
-
- public Guid[] LocalTrailerIds { get; set; }
- public Guid[] RemoteTrailerIds { get; set; }
-
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// airdate, dvd or absolute
- /// </summary>
- public string DisplayOrder { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public SeriesStatus? Status { get; set; }
-
- /// <summary>
- /// Gets or sets the date last episode added.
- /// </summary>
- /// <value>The date last episode added.</value>
- [IgnoreDataMember]
- public DateTime DateLastEpisodeAdded
- {
- get
- {
- return DateLastMediaAdded ?? DateTime.MinValue;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- public override string CreatePresentationUniqueKey()
- {
- if (LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping)
- {
- var userdatakeys = GetUserDataKeys();
-
- if (userdatakeys.Count > 1)
- {
- return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
- }
- }
-
- return base.CreatePresentationUniqueKey();
- }
-
- private string AddLibrariesToPresentationUniqueKey(string key)
- {
- var lang = GetPreferredMetadataLanguage();
- if (!string.IsNullOrWhiteSpace(lang))
- {
- key += "-" + lang;
- }
-
- var folders = LibraryManager.GetCollectionFolders(this)
- .Select(i => i.Id.ToString("N"))
- .ToArray();
-
- if (folders.Length == 0)
- {
- return key;
- }
-
- return key + "-" + string.Join("-", folders);
- }
-
- private static string GetUniqueSeriesKey(BaseItem series)
- {
- return series.GetPresentationUniqueKey();
- }
-
- public override int GetChildCount(User user)
- {
- var seriesKey = GetUniqueSeriesKey(this);
-
- var result = LibraryManager.GetCount(new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Season).Name },
- IsVirtualItem = false,
- Limit = 0,
- DtoOptions = new Dto.DtoOptions(false)
- {
- EnableImages = false
- }
- });
-
- return result;
- }
-
- public override int GetRecursiveChildCount(User user)
- {
- var seriesKey = GetUniqueSeriesKey(this);
-
- var query = new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- DtoOptions = new Dto.DtoOptions(false)
- {
- EnableImages = false
- }
- };
-
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
- }
- query.IsVirtualItem = false;
- query.Limit = 0;
- var totalRecordCount = LibraryManager.GetCount(query);
-
- return totalRecordCount;
- }
-
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- var key = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
-
- key = this.GetProviderId(MetadataProviders.Tvdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
-
- return list;
- }
-
- public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- return GetSeasons(user, new DtoOptions(true));
- }
-
- public List<BaseItem> GetSeasons(User user, DtoOptions options)
- {
- var query = new InternalItemsQuery(user)
- {
- DtoOptions = options
- };
-
- SetSeasonQueryOptions(query, user);
-
- return LibraryManager.GetItemList(query);
- }
-
- private void SetSeasonQueryOptions(InternalItemsQuery query, User user)
- {
- var config = user.Configuration;
-
- var seriesKey = GetUniqueSeriesKey(this);
-
- query.AncestorWithPresentationUniqueKey = null;
- query.SeriesPresentationUniqueKey = seriesKey;
- query.IncludeItemTypes = new[] { typeof(Season).Name };
- query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
-
- if (!config.DisplayMissingEpisodes)
- {
- query.IsMissing = false;
- }
- }
-
- protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
- {
- if (query.User == null)
- {
- return base.GetItemsInternal(query);
- }
-
- var user = query.User;
-
- if (query.Recursive)
- {
- var seriesKey = GetUniqueSeriesKey(this);
-
- query.AncestorWithPresentationUniqueKey = null;
- query.SeriesPresentationUniqueKey = seriesKey;
- if (query.OrderBy.Length == 0)
- {
- query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
- }
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name };
- }
- query.IsVirtualItem = false;
- return LibraryManager.GetItemsResult(query);
- }
-
- SetSeasonQueryOptions(query, user);
-
- return LibraryManager.GetItemsResult(query);
- }
-
- public IEnumerable<BaseItem> GetEpisodes(User user, DtoOptions options)
- {
- var seriesKey = GetUniqueSeriesKey(this);
-
- var query = new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- DtoOptions = options
- };
- var config = user.Configuration;
- if (!config.DisplayMissingEpisodes)
- {
- query.IsMissing = false;
- }
-
- var allItems = LibraryManager.GetItemList(query);
-
- var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
-
- var allEpisodes = allItems.OfType<Season>()
- .SelectMany(i => i.GetEpisodes(this, user, allSeriesEpisodes, options))
- .Reverse();
-
- // Specials could appear twice based on above - once in season 0, once in the aired season
- // This depends on settings for that series
- // When this happens, remove the duplicate from season 0
-
- return allEpisodes.DistinctBy(i => i.Id).Reverse();
- }
-
- public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
- {
- // Refresh bottom up, children first, then the boxset
- // By then hopefully the movies within will have Tmdb collection values
- var items = GetRecursiveChildren();
-
- var totalItems = items.Count;
- var numComplete = 0;
-
- // Refresh current item
- await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
- // Refresh seasons
- foreach (var item in items)
- {
- if (!(item is Season))
- {
- continue;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (refreshOptions.RefreshItem(item))
- {
- await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= totalItems;
- progress.Report(percent * 100);
- }
-
- // Refresh episodes and other children
- foreach (var item in items)
- {
- if ((item is Season))
- {
- continue;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var skipItem = false;
-
- var episode = item as Episode;
-
- if (episode != null
- && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh
- && !refreshOptions.ReplaceAllMetadata
- && episode.IsMissingEpisode
- && episode.LocationType == LocationType.Virtual
- && episode.PremiereDate.HasValue
- && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30)
- {
- skipItem = true;
- }
-
- if (!skipItem)
- {
- if (refreshOptions.RefreshItem(item))
- {
- await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= totalItems;
- progress.Report(percent * 100);
- }
-
- refreshOptions = new MetadataRefreshOptions(refreshOptions);
- refreshOptions.IsPostRecursiveRefresh = true;
- await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
- }
-
- public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options)
- {
- var queryFromSeries = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons;
-
- // add optimization when this setting is not enabled
- var seriesKey = queryFromSeries ?
- GetUniqueSeriesKey(this) :
- GetUniqueSeriesKey(parentSeason);
-
- var query = new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
- SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
- IncludeItemTypes = new[] { typeof(Episode).Name },
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- DtoOptions = options
- };
- if (user != null)
- {
- var config = user.Configuration;
- if (!config.DisplayMissingEpisodes)
- {
- query.IsMissing = false;
- }
- }
-
- var allItems = LibraryManager.GetItemList(query);
-
- return GetSeasonEpisodes(parentSeason, user, allItems, options);
- }
-
- public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, IEnumerable<BaseItem> allSeriesEpisodes, DtoOptions options)
- {
- if (allSeriesEpisodes == null)
- {
- return GetSeasonEpisodes(parentSeason, user, options);
- }
-
- var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons);
-
- var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder;
-
- return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending).ToList();
- }
-
- /// <summary>
- /// Filters the episodes by season.
- /// </summary>
- public static IEnumerable<BaseItem> FilterEpisodesBySeason(IEnumerable<BaseItem> episodes, Season parentSeason, bool includeSpecials)
- {
- var seasonNumber = parentSeason.IndexNumber;
- var seasonPresentationKey = GetUniqueSeriesKey(parentSeason);
-
- var supportSpecialsInSeason = includeSpecials && seasonNumber.HasValue && seasonNumber.Value != 0;
-
- return episodes.Where(episode =>
- {
- var episodeItem = (Episode)episode;
-
- var currentSeasonNumber = supportSpecialsInSeason ? episodeItem.AiredSeasonNumber : episode.ParentIndexNumber;
- if (currentSeasonNumber.HasValue && seasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber.Value)
- {
- return true;
- }
-
- if (!currentSeasonNumber.HasValue && !seasonNumber.HasValue && parentSeason.LocationType == LocationType.Virtual)
- {
- return true;
- }
-
- var season = episodeItem.Season;
- return season != null && string.Equals(GetUniqueSeriesKey(season), seasonPresentationKey, StringComparison.OrdinalIgnoreCase);
- });
- }
-
- /// <summary>
- /// Filters the episodes by season.
- /// </summary>
- public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
- {
- if (!includeSpecials || seasonNumber < 1)
- {
- return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
- }
-
- return episodes.Where(i =>
- {
- var episode = i;
-
- if (episode != null)
- {
- var currentSeasonNumber = episode.AiredSeasonNumber;
-
- return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber;
- }
-
- return false;
- });
- }
-
-
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- return config.BlockUnratedItems.Contains(UnratedItem.Series);
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Series;
- }
-
- public SeriesInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<SeriesInfo>();
-
- return info;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (!ProductionYear.HasValue)
- {
- var info = LibraryManager.ParseName(Name);
-
- var yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- }
-
- return hasChanges;
- }
-
- public override List<ExternalUrl> GetRelatedUrls()
- {
- var list = base.GetRelatedUrls();
-
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(imdbId))
- {
- list.Add(new ExternalUrl
- {
- Name = "Trakt",
- Url = string.Format("https://trakt.tv/shows/{0}", imdbId)
- });
- }
-
- return list;
- }
-
- [IgnoreDataMember]
- public override bool StopRefreshIfLocalMetadataFound
- {
- get
- {
- // Need people id's from internet metadata
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/TagExtensions.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs
deleted file mode 100644
index e5d8f35d9..000000000
--- a/MediaBrowser.Controller/Entities/TagExtensions.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- public static class TagExtensions
- {
- public static void AddTag(this BaseItem item, string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- var current = item.Tags;
-
- if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- 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
deleted file mode 100644
index c5144aadf..000000000
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Trailer
- /// </summary>
- public class Trailer : Video, IHasLookupInfo<TrailerInfo>
- {
- public Trailer()
- {
- RemoteTrailers = new List<MediaUrl>();
- TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
- }
-
- public List<TrailerType> TrailerTypes { get; set; }
-
- public List<MediaUrl> RemoteTrailers { get; set; }
-
- [IgnoreDataMember]
- public bool IsLocalTrailer
- {
- get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.Trailer;
- }
-
- public TrailerInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<TrailerInfo>();
-
- info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer);
-
- if (!IsInMixedFolder && LocationType == LocationType.FileSystem)
- {
- info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
- }
-
- return info;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (!ProductionYear.HasValue)
- {
- var info = LibraryManager.ParseName(Name);
-
- var yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- else
- {
- // Try to get the year from the folder name
- if (!IsInMixedFolder)
- {
- info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
-
- yearInName = info.Year;
-
- if (yearInName.HasValue)
- {
- ProductionYear = yearInName;
- hasChanges = true;
- }
- }
- }
- }
-
- return hasChanges;
- }
-
- public override List<ExternalUrl> GetRelatedUrls()
- {
- var list = base.GetRelatedUrls();
-
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(imdbId))
- {
- list.Add(new ExternalUrl
- {
- Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
- });
- }
-
- return list;
- }
-
- [IgnoreDataMember]
- public override bool StopRefreshIfLocalMetadataFound
- {
- get
- {
- // Need people id's from internet metadata
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
deleted file mode 100644
index 821327b7f..000000000
--- a/MediaBrowser.Controller/Entities/User.cs
+++ /dev/null
@@ -1,351 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class User
- /// </summary>
- public class User : BaseItem
- {
- public static IUserManager UserManager { get; set; }
- public static IXmlSerializer XmlSerializer { get; set; }
-
- /// <summary>
- /// From now on all user paths will be Id-based.
- /// This is for backwards compatibility.
- /// </summary>
- public bool UsesIdForConfigurationPath { get; set; }
-
- /// <summary>
- /// Gets or sets the password.
- /// </summary>
- /// <value>The password.</value>
- public string Password { get; set; }
- public string EasyPassword { get; set; }
- public string Salt { get; set; }
-
- public string ConnectUserName { get; set; }
- public string ConnectUserId { get; set; }
- public UserLinkType? ConnectLinkType { get; set; }
- public string ConnectAccessKey { get; set; }
-
- // Strictly to remove IgnoreDataMember
- public override ItemImageInfo[] ImageInfos
- {
- get
- {
- return base.ImageInfos;
- }
- set
- {
- base.ImageInfos = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- [IgnoreDataMember]
- public override string Path
- {
- get
- {
- // Return this so that metadata providers will look in here
- return ConfigurationDirectoryPath;
- }
- set
- {
- base.Path = value;
- }
- }
-
- private string _name;
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public override string Name
- {
- get
- {
- return _name;
- }
- set
- {
- _name = value;
-
- // lazy load this again
- SortName = null;
- }
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
- /// Gets the root folder.
- /// </summary>
- /// <value>The root folder.</value>
- [IgnoreDataMember]
- public Folder RootFolder
- {
- get
- {
- return LibraryManager.GetUserRootFolder();
- }
- }
-
- /// <summary>
- /// Gets or sets the last login date.
- /// </summary>
- /// <value>The last login date.</value>
- public DateTime? LastLoginDate { get; set; }
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime? LastActivityDate { get; set; }
-
- private volatile UserConfiguration _config;
- private readonly object _configSyncLock = new object();
- [IgnoreDataMember]
- public UserConfiguration Configuration
- {
- get
- {
- if (_config == null)
- {
- lock (_configSyncLock)
- {
- if (_config == null)
- {
- _config = UserManager.GetUserConfiguration(this);
- }
- }
- }
-
- return _config;
- }
- set { _config = value; }
- }
-
- private volatile UserPolicy _policy;
- private readonly object _policySyncLock = new object();
- [IgnoreDataMember]
- public UserPolicy Policy
- {
- get
- {
- if (_policy == null)
- {
- lock (_policySyncLock)
- {
- if (_policy == null)
- {
- _policy = UserManager.GetUserPolicy(this);
- }
- }
- }
-
- return _policy;
- }
- set { _policy = value; }
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public Task Rename(string newName)
- {
- if (string.IsNullOrEmpty(newName))
- {
- throw new ArgumentNullException("newName");
- }
-
- // If only the casing is changing, leave the file system alone
- if (!UsesIdForConfigurationPath && !string.Equals(newName, Name, StringComparison.OrdinalIgnoreCase))
- {
- UsesIdForConfigurationPath = true;
-
- // Move configuration
- var newConfigDirectory = GetConfigurationDirectoryPath(newName);
- var oldConfigurationDirectory = ConfigurationDirectoryPath;
-
- // Exceptions will be thrown if these paths already exist
- if (FileSystem.DirectoryExists(newConfigDirectory))
- {
- FileSystem.DeleteDirectory(newConfigDirectory, true);
- }
-
- if (FileSystem.DirectoryExists(oldConfigurationDirectory))
- {
- FileSystem.MoveDirectory(oldConfigurationDirectory, newConfigDirectory);
- }
- else
- {
- FileSystem.CreateDirectory(newConfigDirectory);
- }
- }
-
- Name = newName;
-
- return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem))
- {
- ReplaceAllMetadata = true,
- ImageRefreshMode = ImageRefreshMode.FullRefresh,
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ForceSave = true
-
- }, CancellationToken.None);
- }
-
- public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- UserManager.UpdateUser(this);
- }
-
- /// <summary>
- /// Gets the path to the user's configuration directory
- /// </summary>
- /// <value>The configuration directory path.</value>
- [IgnoreDataMember]
- public string ConfigurationDirectoryPath
- {
- get
- {
- return GetConfigurationDirectoryPath(Name);
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- /// <summary>
- /// Gets the configuration directory path.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- private string GetConfigurationDirectoryPath(string username)
- {
- var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath;
-
- // Legacy
- if (!UsesIdForConfigurationPath)
- {
- if (string.IsNullOrEmpty(username))
- {
- throw new ArgumentNullException("username");
- }
-
- var safeFolderName = FileSystem.GetValidFilename(username);
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, safeFolderName);
- }
-
- return System.IO.Path.Combine(parentPath, Id.ToString("N"));
- }
-
- public bool IsParentalScheduleAllowed()
- {
- return IsParentalScheduleAllowed(DateTime.UtcNow);
- }
-
- public bool IsParentalScheduleAllowed(DateTime date)
- {
- var schedules = Policy.AccessSchedules;
-
- if (schedules.Length == 0)
- {
- return true;
- }
-
- foreach (var i in schedules)
- {
- if (IsParentalScheduleAllowed(i, date))
- {
- return true;
- }
- }
- return false;
- }
-
- private bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date)
- {
- if (date.Kind != DateTimeKind.Utc)
- {
- throw new ArgumentException("Utc date expected");
- }
-
- var localTime = date.ToLocalTime();
-
- return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
- IsWithinTime(schedule, localTime);
- }
-
- private bool IsWithinTime(AccessSchedule schedule, DateTime localTime)
- {
- var hour = localTime.TimeOfDay.TotalHours;
-
- return hour >= schedule.StartHour && hour <= schedule.EndHour;
- }
-
- public bool IsFolderGrouped(Guid id)
- {
- foreach (var i in Configuration.GroupedFolders)
- {
- if (new Guid(i) == id)
- {
- return true;
- }
- }
- return false;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
deleted file mode 100644
index 0e1326949..000000000
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using System;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class UserItemData
- /// </summary>
- public class UserItemData
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public Guid UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
-
- /// <summary>
- /// The _rating
- /// </summary>
- private double? _rating;
- /// <summary>
- /// Gets or sets the users 0-10 rating
- /// </summary>
- /// <value>The rating.</value>
- /// <exception cref="System.ArgumentOutOfRangeException">Rating;A 0 to 10 rating is required for UserItemData.</exception>
- public double? Rating
- {
- get
- {
- return _rating;
- }
- set
- {
- if (value.HasValue)
- {
- if (value.Value < 0 || value.Value > 10)
- {
- throw new ArgumentOutOfRangeException("value", "A 0 to 10 rating is required for UserItemData.");
- }
- }
-
- _rating = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the playback position ticks.
- /// </summary>
- /// <value>The playback position ticks.</value>
- public long PlaybackPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the play count.
- /// </summary>
- /// <value>The play count.</value>
- public int PlayCount { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>true</c> if this instance is favorite; otherwise, <c>false</c>.</value>
- public bool IsFavorite { get; set; }
-
- /// <summary>
- /// Gets or sets the last played date.
- /// </summary>
- /// <value>The last played date.</value>
- public DateTime? LastPlayedDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="UserItemData" /> is played.
- /// </summary>
- /// <value><c>true</c> if played; otherwise, <c>false</c>.</value>
- public bool Played { get; set; }
- /// <summary>
- /// Gets or sets the index of the audio stream.
- /// </summary>
- /// <value>The index of the audio stream.</value>
- public int? AudioStreamIndex { get; set; }
- /// <summary>
- /// Gets or sets the index of the subtitle stream.
- /// </summary>
- /// <value>The index of the subtitle stream.</value>
- public int? SubtitleStreamIndex { get; set; }
-
- public const double MinLikeValue = 6.5;
-
- /// <summary>
- /// This is an interpreted property to indicate likes or dislikes
- /// This should never be serialized.
- /// </summary>
- /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool? Likes
- {
- get
- {
- if (Rating != null)
- {
- return Rating >= MinLikeValue;
- }
-
- return null;
- }
- set
- {
- if (value.HasValue)
- {
- Rating = value.Value ? 10 : 1;
- }
- else
- {
- Rating = null;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
deleted file mode 100644
index 2ed3bf20f..000000000
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Special class used for User Roots. Children contain actual ones defined for this user
- /// PLUS the virtual folders from the physical root (added by plug-ins).
- /// </summary>
- public class UserRootFolder : Folder
- {
- private List<Guid> _childrenIds = null;
- private readonly object _childIdsLock = new object();
- protected override List<BaseItem> LoadChildren()
- {
- lock (_childIdsLock)
- {
- if (_childrenIds == null)
- {
- var list = base.LoadChildren();
- _childrenIds = list.Select(i => i.Id).ToList();
- return list;
- }
-
- return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- private void ClearCache()
- {
- lock (_childIdsLock)
- {
- _childrenIds = null;
- }
- }
-
- protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
- {
- if (query.Recursive)
- {
- return QueryRecursive(query);
- }
-
- var result = UserViewManager.GetUserViews(new UserViewQuery
- {
- UserId = query.User.Id.ToString("N"),
- PresetViews = query.PresetViews
-
- }, CancellationToken.None).Result;
-
- var user = query.User;
- Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
-
- return PostFilterAndSort(result.Where(filter), query, true, true);
- }
-
- public override int GetChildCount(User user)
- {
- return GetChildren(user, true).Count;
- }
-
- [IgnoreDataMember]
- protected override bool SupportsShortcutChildren
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool IsPreSorted
- {
- get
- {
- return true;
- }
- }
-
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
- {
- var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
- list.AddRange(LibraryManager.RootFolder.VirtualChildren);
-
- return list;
- }
-
- public override bool BeforeMetadataRefresh()
- {
- ClearCache();
-
- var hasChanges = base.BeforeMetadataRefresh();
-
- if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
- {
- Name = "Media Folders";
- hasChanges = true;
- }
-
- return hasChanges;
- }
-
- protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- ClearCache();
-
- return base.GetNonCachedChildren(directoryService);
- }
-
- protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- ClearCache();
-
- await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)
- .ConfigureAwait(false);
-
- ClearCache();
-
- // Not the best way to handle this, but it solves an issue
- // CollectionFolders aren't always getting saved after changes
- // This means that grabbing the item by Id may end up returning the old one
- // Fix is in two places - make sure the folder gets saved
- // And here to remedy it for affected users.
- // In theory this can be removed eventually.
- foreach (var item in Children)
- {
- LibraryManager.RegisterItem(item);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
deleted file mode 100644
index 2152e65cf..000000000
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class UserView : Folder
- {
- public string ViewType { get; set; }
- public Guid DisplayParentId { get; set; }
-
- public Guid? UserId { get; set; }
-
- public static ITVSeriesManager TVSeriesManager;
- public static IPlaylistManager PlaylistManager;
-
- public override IEnumerable<Guid> GetIdsForAncestorQuery()
- {
- var list = new List<Guid>();
-
- if (DisplayParentId != Guid.Empty)
- {
- list.Add(DisplayParentId);
- }
- else if (ParentId != Guid.Empty)
- {
- list.Add(ParentId);
- }
- else
- {
- list.Add(Id);
- }
- return list;
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return false;
- }
- }
-
- //public override double? GetDefaultPrimaryImageAspectRatio()
- //{
- // double value = 16;
- // value /= 9;
-
- // return value;
- //}
-
- public override int GetChildCount(User user)
- {
- return GetChildren(user, true).Count;
- }
-
- protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
- {
- var parent = this as Folder;
-
- if (DisplayParentId != Guid.Empty)
- {
- parent = LibraryManager.GetItemById(DisplayParentId) as Folder ?? parent;
- }
- else if (ParentId != Guid.Empty)
- {
- parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
- }
-
- return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager, PlaylistManager)
- .GetUserItems(parent, this, ViewType, query).Result;
- }
-
- public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- var result = GetItemList(new InternalItemsQuery
- {
- User = user,
- EnableTotalRecordCount = false,
- DtoOptions = new DtoOptions(true)
-
- });
-
- return result.ToList();
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
- {
- var result = GetItemList(new InternalItemsQuery
- {
- User = user,
- Recursive = true,
- EnableTotalRecordCount = false,
-
- ForceDirect = true,
-
- DtoOptions = query.DtoOptions
-
- });
-
- return result.Where(i => UserViewBuilder.FilterItem(i, query));
- }
-
- protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
- {
- return GetChildren(user, false);
- }
-
- public static bool IsUserSpecific(Folder folder)
- {
- var standaloneTypes = new List<string>
- {
- CollectionType.Playlists
- };
-
- var collectionFolder = folder as ICollectionFolder;
-
- if (collectionFolder == null)
- {
- return false;
- }
-
- var supportsUserSpecific = folder as ISupportsUserSpecificView;
- if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView)
- {
- return true;
- }
-
- return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
- }
-
- public static bool IsEligibleForGrouping(Folder folder)
- {
- var collectionFolder = folder as ICollectionFolder;
- return collectionFolder != null && IsEligibleForGrouping(collectionFolder.CollectionType);
- }
-
- public static bool IsEligibleForGrouping(string viewType)
- {
- var types = new[]
- {
- CollectionType.Movies,
- CollectionType.TvShows,
- string.Empty
- };
-
- return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- }
-
- public static bool EnableOriginalFolder(string viewType)
- {
- var types = new[]
- {
- CollectionType.Games,
- CollectionType.Books,
- CollectionType.MusicVideos,
- CollectionType.HomeVideos,
- CollectionType.Photos,
- CollectionType.Music,
- CollectionType.BoxSets
- };
-
- return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- }
-
- protected override Task ValidateChildrenInternal(IProgress<double> progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService)
- {
- return Task.FromResult(true);
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
deleted file mode 100644
index 97b96127a..000000000
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ /dev/null
@@ -1,1778 +0,0 @@
-using MediaBrowser.Controller.Channels;
-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.Playlists;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- public class UserViewBuilder
- {
- private readonly IChannelManager _channelManager;
- private readonly ILiveTvManager _liveTvManager;
- private readonly IUserViewManager _userViewManager;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IUserDataManager _userDataManager;
- private readonly ITVSeriesManager _tvSeriesManager;
- private readonly IServerConfigurationManager _config;
- private readonly IPlaylistManager _playlistManager;
-
- public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config, IPlaylistManager playlistManager)
- {
- _userViewManager = userViewManager;
- _liveTvManager = liveTvManager;
- _channelManager = channelManager;
- _libraryManager = libraryManager;
- _logger = logger;
- _userDataManager = userDataManager;
- _tvSeriesManager = tvSeriesManager;
- _config = config;
- _playlistManager = playlistManager;
- }
-
- public async Task<QueryResult<BaseItem>> GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query)
- {
- var user = query.User;
-
- //if (query.IncludeItemTypes != null &&
- // query.IncludeItemTypes.Length == 1 &&
- // string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
- //{
- // if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
- // {
- // return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
- // }
- //}
-
- switch (viewType)
- {
- case CollectionType.Channels:
- {
- var result = await _channelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = user == null ? null : user.Id.ToString("N"),
- Limit = query.Limit,
- StartIndex = query.StartIndex
-
- }, CancellationToken.None).ConfigureAwait(false);
-
- return GetResult(result);
- }
-
- case SpecialFolder.LiveTvChannels:
- {
- var result = _liveTvManager.GetInternalChannels(new LiveTvChannelQuery
- {
- UserId = query.User.Id.ToString("N"),
- Limit = query.Limit,
- StartIndex = query.StartIndex
-
- }, new DtoOptions(), CancellationToken.None);
-
- return GetResult(result);
- }
-
- case SpecialFolder.LiveTvNowPlaying:
- {
- var result = _liveTvManager.GetRecommendedProgramsInternal(new RecommendedProgramQuery
- {
- UserId = query.User.Id.ToString("N"),
- Limit = query.Limit,
- IsAiring = true
-
- }, new Dto.DtoOptions(), CancellationToken.None);
-
- return GetResult(result);
- }
-
- case SpecialFolder.LiveTvRecordingGroups:
- {
- var result = await _liveTvManager.GetInternalRecordings(new RecordingQuery
- {
- UserId = query.User.Id.ToString("N"),
- Status = RecordingStatus.Completed,
- Limit = query.Limit,
- StartIndex = query.StartIndex
-
- }, new DtoOptions(), CancellationToken.None).ConfigureAwait(false);
-
- return GetResult(result);
- }
-
- case CollectionType.LiveTv:
- {
- return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false);
- }
-
- case CollectionType.Photos:
- case CollectionType.Books:
- case CollectionType.HomeVideos:
- case CollectionType.Games:
- case CollectionType.MusicVideos:
- {
- if (query.Recursive)
- {
- query.Recursive = true;
- query.Parent = queryParent;
- query.SetUser(user);
-
- return _libraryManager.GetItemsResult(query);
- }
- return GetResult(queryParent.GetChildren(user, true), queryParent, query);
- }
-
- case CollectionType.Folders:
- return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query);
-
- case CollectionType.Playlists:
- return GetPlaylistsView(queryParent, user, query);
-
- case CollectionType.BoxSets:
- return GetBoxsetView(queryParent, user, query);
-
- case CollectionType.TvShows:
- return GetTvView(queryParent, user, query);
-
- case CollectionType.Movies:
- return GetMovieFolders(queryParent, user, query);
-
- case SpecialFolder.TvShowSeries:
- return GetTvSeries(queryParent, user, query);
-
- case SpecialFolder.TvGenres:
- return GetTvGenres(queryParent, user, query);
-
- case SpecialFolder.TvGenre:
- return GetTvGenreItems(queryParent, displayParent, user, query);
-
- case SpecialFolder.TvResume:
- return GetTvResume(queryParent, user, query);
-
- case SpecialFolder.TvNextUp:
- return GetTvNextUp(queryParent, query);
-
- case SpecialFolder.TvLatest:
- return GetTvLatest(queryParent, user, query);
-
- case SpecialFolder.MovieFavorites:
- return GetFavoriteMovies(queryParent, user, query);
-
- case SpecialFolder.MovieLatest:
- return GetMovieLatest(queryParent, user, query);
-
- case SpecialFolder.MovieGenres:
- return GetMovieGenres(queryParent, user, query);
-
- case SpecialFolder.MovieGenre:
- return GetMovieGenreItems(queryParent, displayParent, user, query);
-
- case SpecialFolder.MovieResume:
- return GetMovieResume(queryParent, user, query);
-
- case SpecialFolder.MovieMovies:
- return GetMovieMovies(queryParent, user, query);
-
- case SpecialFolder.MovieCollections:
- return GetMovieCollections(queryParent, user, query);
-
- case SpecialFolder.TvFavoriteEpisodes:
- return GetFavoriteEpisodes(queryParent, user, query);
-
- case SpecialFolder.TvFavoriteSeries:
- return GetFavoriteSeries(queryParent, user, query);
-
- case CollectionType.Music:
- return GetMusicFolders(queryParent, user, query);
-
- case SpecialFolder.MusicGenres:
- return GetMusicGenres(queryParent, user, query);
-
- case SpecialFolder.MusicLatest:
- return GetMusicLatest(queryParent, user, query);
-
- case SpecialFolder.MusicPlaylists:
- return GetMusicPlaylists(queryParent, user, query);
-
- case SpecialFolder.MusicAlbums:
- return GetMusicAlbums(queryParent, user, query);
-
- case SpecialFolder.MusicAlbumArtists:
- return GetMusicAlbumArtists(queryParent, user, query);
-
- case SpecialFolder.MusicArtists:
- return GetMusicArtists(queryParent, user, query);
-
- case SpecialFolder.MusicSongs:
- return GetMusicSongs(queryParent, user, query);
-
- case SpecialFolder.MusicFavorites:
- return GetMusicFavorites(queryParent, user, query);
-
- case SpecialFolder.MusicFavoriteAlbums:
- return GetFavoriteAlbums(queryParent, user, query);
-
- case SpecialFolder.MusicFavoriteArtists:
- return GetFavoriteArtists(queryParent, user, query);
-
- case SpecialFolder.MusicFavoriteSongs:
- return GetFavoriteSongs(queryParent, user, query);
-
- default:
- {
- if (queryParent is UserView)
- {
- return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), queryParent, query);
- }
- return GetResult(queryParent.GetChildren(user, true), queryParent, query);
- }
- }
- }
-
- private QueryResult<BaseItem> GetMusicFolders(Folder parent, User user, InternalItemsQuery query)
- {
- if (query.Recursive)
- {
- query.Recursive = true;
- query.SetUser(user);
-
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(Audio.Audio).Name, typeof(MusicVideo).Name };
- }
-
- return parent.QueryRecursive(query);
- }
-
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.MusicLatest, "Latest", "0", parent));
- list.Add(GetUserView(SpecialFolder.MusicPlaylists, "Playlists", "1", parent));
- list.Add(GetUserView(SpecialFolder.MusicAlbums, "Albums", "2", parent));
- list.Add(GetUserView(SpecialFolder.MusicAlbumArtists, "HeaderAlbumArtists", "3", parent));
- list.Add(GetUserView(SpecialFolder.MusicArtists, "Artists", "4", parent));
- list.Add(GetUserView(SpecialFolder.MusicSongs, "Songs", "5", parent));
- list.Add(GetUserView(SpecialFolder.MusicGenres, "Genres", "6", parent));
- list.Add(GetUserView(SpecialFolder.MusicFavorites, "Favorites", "7", parent));
-
- return GetResult(list, parent, query);
- }
-
- private QueryResult<BaseItem> GetMusicFavorites(Folder parent, User user, InternalItemsQuery query)
- {
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.MusicFavoriteAlbums, "HeaderFavoriteAlbums", "0", parent));
- list.Add(GetUserView(SpecialFolder.MusicFavoriteArtists, "HeaderFavoriteArtists", "1", parent));
- list.Add(GetUserView(SpecialFolder.MusicFavoriteSongs, "HeaderFavoriteSongs", "2", parent));
-
- return GetResult(list, parent, query);
- }
-
- private QueryResult<BaseItem> GetMusicGenres(Folder parent, User user, InternalItemsQuery query)
- {
- var result = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
- {
- AncestorIds = new[] { parent.Id.ToString("N") },
- StartIndex = query.StartIndex,
- Limit = query.Limit
- });
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = result.TotalRecordCount,
- Items = result.Items.Select(i => i.Item1).ToArray()
- };
- }
-
- private QueryResult<BaseItem> GetMusicAlbumArtists(Folder parent, User user, InternalItemsQuery query)
- {
- var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
- {
- AncestorIds = new[] { parent.Id.ToString("N") },
- StartIndex = query.StartIndex,
- Limit = query.Limit
- });
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
- };
- }
-
- private QueryResult<BaseItem> GetMusicArtists(Folder parent, User user, InternalItemsQuery query)
- {
- var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
- {
- AncestorIds = new[] { parent.Id.ToString("N") },
- StartIndex = query.StartIndex,
- Limit = query.Limit
- });
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
- };
- }
-
- private QueryResult<BaseItem> GetFavoriteArtists(Folder parent, User user, InternalItemsQuery query)
- {
- var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
- {
- AncestorIds = new[] { parent.Id.ToString("N") },
- StartIndex = query.StartIndex,
- Limit = query.Limit,
- IsFavorite = true
- });
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = artists.TotalRecordCount,
- Items = artists.Items.Select(i => i.Item1).ToArray()
- };
- }
-
- private QueryResult<BaseItem> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query)
- {
- query.Parent = null;
- query.IncludeItemTypes = new[] { typeof(Playlist).Name };
- query.SetUser(user);
- query.Recursive = true;
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetMusicSongs(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(Audio.Audio).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query)
- {
- var items = _userViewManager.GetLatestItems(new LatestItemsQuery
- {
- UserId = user.Id.ToString("N"),
- Limit = GetSpecialItemsLimit(),
- IncludeItemTypes = new[] { typeof(Audio.Audio).Name },
- ParentId = parent == null ? null : parent.Id.ToString("N"),
- GroupItems = true
-
- }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null);
-
- query.OrderBy = new Tuple<string, SortOrder>[] { };
-
- return PostFilterAndSort(items, parent, null, query, false, true);
- }
-
- private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Audio.Audio).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private int GetSpecialItemsLimit()
- {
- return 50;
- }
-
- private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
- {
- if (query.Recursive)
- {
- query.Recursive = true;
- query.SetUser(user);
-
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(Movie).Name, typeof(BoxSet).Name };
- }
-
- return parent.QueryRecursive(query);
- }
-
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent));
- list.Add(GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent));
- list.Add(GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent));
- list.Add(GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent));
- list.Add(GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent));
-
- return GetResult(list, parent, query);
- }
-
- private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Series).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetMovieCollections(Folder parent, User user, InternalItemsQuery query)
- {
- return GetBoxsetView(parent, user, query);
- }
-
- private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
- {
- query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
-
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
-
- return ConvertToResult(_libraryManager.GetItemList(query));
- }
-
- private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
- {
- query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
- query.IsResumable = true;
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
-
- return ConvertToResult(_libraryManager.GetItemList(query));
- }
-
- private QueryResult<BaseItem> ConvertToResult(List<BaseItem> items)
- {
- var arr = items.ToArray();
- return new QueryResult<BaseItem>
- {
- Items = arr,
- TotalRecordCount = arr.Length
- };
- }
-
- private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
- {
- var genres = parent.QueryRecursive(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Movie).Name },
- Recursive = true,
- EnableTotalRecordCount = false
-
- }).Items
- .SelectMany(i => i.Genres)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetGenre(i);
- }
- catch
- {
- // Full exception logged at lower levels
- _logger.Error("Error getting genre");
- return null;
- }
-
- })
- .Where(i => i != null)
- .Select(i => GetUserViewWithName(i.Name, SpecialFolder.MovieGenre, i.SortName, parent));
-
- return GetResult(genres, parent, query);
- }
-
- private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = queryParent;
- query.GenreIds = new[] { displayParent.Id.ToString("N") };
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetPlaylistsView(Folder parent, User user, InternalItemsQuery query)
- {
- return GetResult(_playlistManager.GetPlaylists(user.Id.ToString("N")), parent, query);
- }
-
- private QueryResult<BaseItem> GetBoxsetView(Folder parent, User user, InternalItemsQuery query)
- {
- query.Parent = null;
- query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
- query.SetUser(user);
- query.Recursive = true;
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetTvView(Folder parent, User user, InternalItemsQuery query)
- {
- if (query.Recursive)
- {
- query.Recursive = true;
- query.SetUser(user);
-
- if (query.IncludeItemTypes.Length == 0)
- {
- query.IncludeItemTypes = new[] { typeof(Series).Name, typeof(Season).Name, typeof(Episode).Name };
- }
-
- return parent.QueryRecursive(query);
- }
-
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent));
- list.Add(GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent));
- list.Add(GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent));
- list.Add(GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent));
-
- return GetResult(list, parent, query);
- }
-
- private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
- {
- query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
-
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
- query.IsVirtualItem = false;
-
- return ConvertToResult(_libraryManager.GetItemList(query));
- }
-
- private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
- {
- var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty });
-
- var result = _tvSeriesManager.GetNextUp(new NextUpQuery
- {
- Limit = query.Limit,
- StartIndex = query.StartIndex,
- UserId = query.User.Id.ToString("N")
-
- }, parentFolders, query.DtoOptions);
-
- return result;
- }
-
- private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
- {
- query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
- query.IsResumable = true;
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
- query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
-
- return ConvertToResult(_libraryManager.GetItemList(query));
- }
-
- private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = parent;
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(Series).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
- {
- var genres = parent.QueryRecursive(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- EnableTotalRecordCount = false
-
- }).Items
- .SelectMany(i => i.Genres)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetGenre(i);
- }
- catch
- {
- // Full exception logged at lower levels
- _logger.Error("Error getting genre");
- return null;
- }
-
- })
- .Where(i => i != null)
- .Select(i => GetUserViewWithName(i.Name, SpecialFolder.TvGenre, i.SortName, parent));
-
- return GetResult(genres, parent, query);
- }
-
- private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
- {
- query.Recursive = true;
- query.Parent = queryParent;
- query.GenreIds = new[] { displayParent.Id.ToString("N") };
- query.SetUser(user);
-
- query.IncludeItemTypes = new[] { typeof(Series).Name };
-
- return _libraryManager.GetItemsResult(query);
- }
-
- private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result)
- where T : BaseItem
- {
- return new QueryResult<BaseItem>
- {
- Items = result.Items,
- TotalRecordCount = result.TotalRecordCount
- };
- }
-
- private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items,
- BaseItem queryParent,
- InternalItemsQuery query)
- where T : BaseItem
- {
- items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
-
- return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config, true, true);
- }
-
- public static bool FilterItem(BaseItem item, InternalItemsQuery query)
- {
- return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager);
- }
-
- private QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
- BaseItem queryParent,
- int? totalRecordLimit,
- InternalItemsQuery query,
- bool collapseBoxSetItems,
- bool enableSorting)
- {
- return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _config, collapseBoxSetItems, enableSorting);
- }
-
- public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
- BaseItem queryParent,
- int? totalRecordLimit,
- InternalItemsQuery query,
- ILibraryManager libraryManager,
- IServerConfigurationManager configurationManager,
- bool collapseBoxSetItems,
- bool enableSorting)
- {
- var user = query.User;
-
- items = FilterVirtualEpisodes(items,
- query.IsMissing,
- query.IsUnaired);
-
- if (collapseBoxSetItems && user != null)
- {
- items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager);
- }
-
- // This must be the last filter
- if (!string.IsNullOrEmpty(query.AdjacentTo))
- {
- items = FilterForAdjacency(items.ToList(), query.AdjacentTo);
- }
-
- return SortAndPage(items, totalRecordLimit, query, libraryManager, enableSorting);
- }
-
- public static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items,
- InternalItemsQuery query,
- BaseItem queryParent,
- User user,
- IServerConfigurationManager configurationManager)
- {
- if (items == null)
- {
- throw new ArgumentNullException("items");
- }
-
- if (CollapseBoxSetItems(query, queryParent, user, configurationManager))
- {
- items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user);
- }
-
- items = ApplyPostCollectionCollapseFilters(query, items, user);
-
- return items;
- }
-
- private static IEnumerable<BaseItem> ApplyPostCollectionCollapseFilters(InternalItemsQuery request,
- IEnumerable<BaseItem> items,
- User user)
- {
- if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
- {
- items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
- }
- if (!string.IsNullOrEmpty(request.NameStartsWith))
- {
- items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
- }
-
- if (!string.IsNullOrEmpty(request.NameLessThan))
- {
- items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
- }
-
- return items;
- }
-
- public static bool CollapseBoxSetItems(InternalItemsQuery query,
- BaseItem queryParent,
- User user,
- IServerConfigurationManager configurationManager)
- {
- // Could end up stuck in a loop like this
- if (queryParent is BoxSet)
- {
- return false;
- }
-
- var param = query.CollapseBoxSetItems;
-
- if (!param.HasValue)
- {
- if (user != null && !configurationManager.Configuration.EnableGroupingIntoCollections)
- {
- return false;
- }
-
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase))
- {
- param = true;
- }
- }
-
- return param.HasValue && param.Value && AllowBoxSetCollapsing(query);
- }
-
- private static bool AllowBoxSetCollapsing(InternalItemsQuery request)
- {
- if (request.IsFavorite.HasValue)
- {
- return false;
- }
- if (request.IsFavoriteOrLiked.HasValue)
- {
- return false;
- }
- if (request.IsLiked.HasValue)
- {
- return false;
- }
- if (request.IsPlayed.HasValue)
- {
- return false;
- }
- if (request.IsResumable.HasValue)
- {
- return false;
- }
- if (request.IsFolder.HasValue)
- {
- return false;
- }
-
- if (request.Genres.Length > 0)
- {
- return false;
- }
-
- if (request.GenreIds.Length > 0)
- {
- return false;
- }
-
- if (request.HasImdbId.HasValue)
- {
- return false;
- }
-
- if (request.HasOfficialRating.HasValue)
- {
- return false;
- }
-
- if (request.HasOverview.HasValue)
- {
- return false;
- }
-
- if (request.HasParentalRating.HasValue)
- {
- return false;
- }
-
- if (request.HasSpecialFeature.HasValue)
- {
- return false;
- }
-
- if (request.HasSubtitles.HasValue)
- {
- return false;
- }
-
- if (request.HasThemeSong.HasValue)
- {
- return false;
- }
-
- if (request.HasThemeVideo.HasValue)
- {
- return false;
- }
-
- if (request.HasTmdbId.HasValue)
- {
- return false;
- }
-
- if (request.HasTrailer.HasValue)
- {
- return false;
- }
-
- if (request.ImageTypes.Length > 0)
- {
- return false;
- }
-
- if (request.Is3D.HasValue)
- {
- return false;
- }
-
- if (request.IsHD.HasValue)
- {
- return false;
- }
-
- if (request.IsInBoxSet.HasValue)
- {
- return false;
- }
-
- if (request.IsLocked.HasValue)
- {
- return false;
- }
-
- if (request.IsPlaceHolder.HasValue)
- {
- return false;
- }
-
- if (request.IsPlayed.HasValue)
- {
- return false;
- }
-
- if (!string.IsNullOrWhiteSpace(request.Person))
- {
- return false;
- }
-
- if (request.PersonIds.Length > 0)
- {
- return false;
- }
-
- if (request.ItemIds.Length > 0)
- {
- return false;
- }
-
- if (request.StudioIds.Length > 0)
- {
- return false;
- }
-
- if (request.GenreIds.Length > 0)
- {
- return false;
- }
-
- if (request.VideoTypes.Length > 0)
- {
- return false;
- }
-
- if (request.Years.Length > 0)
- {
- return false;
- }
-
- if (request.Tags.Length > 0)
- {
- return false;
- }
-
- if (request.OfficialRatings.Length > 0)
- {
- return false;
- }
-
- if (request.MinPlayers.HasValue)
- {
- return false;
- }
-
- if (request.MaxPlayers.HasValue)
- {
- return false;
- }
-
- if (request.MinCommunityRating.HasValue)
- {
- return false;
- }
-
- if (request.MinCriticRating.HasValue)
- {
- return false;
- }
-
- if (request.MinIndexNumber.HasValue)
- {
- return false;
- }
-
- return true;
- }
-
- private static IEnumerable<BaseItem> FilterVirtualEpisodes(
- IEnumerable<BaseItem> items,
- bool? isMissing,
- bool? isUnaired)
- {
- if (isMissing.HasValue)
- {
- var val = isMissing.Value;
- items = items.Where(i =>
- {
- var e = i as Episode;
- if (e != null)
- {
- return e.IsMissingEpisode == val;
- }
- return true;
- });
- }
-
- if (isUnaired.HasValue)
- {
- var val = isUnaired.Value;
- items = items.Where(i =>
- {
- var e = i as Episode;
- if (e != null)
- {
- return e.IsUnaired == val;
- }
- return true;
- });
- }
-
- return items;
- }
-
- public static QueryResult<BaseItem> SortAndPage(IEnumerable<BaseItem> items,
- int? totalRecordLimit,
- InternalItemsQuery query,
- ILibraryManager libraryManager, bool enableSorting)
- {
- items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase);
-
- if (query.OrderBy.Length > 0)
- {
- items = libraryManager.Sort(items, query.User, query.OrderBy);
- }
-
- var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
- var totalCount = itemsArray.Length;
-
- if (query.Limit.HasValue)
- {
- itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
- }
- else if (query.StartIndex.HasValue)
- {
- itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
- }
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = itemsArray
- };
- }
-
- public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
- {
- if (query.ItemIdsFromPersonFilters == null)
- {
- if (query.PersonIds.Length > 0)
- {
- var names = query.PersonIds
- .Select(libraryManager.GetItemById)
- .Select(i => i == null ? null : i.Name)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .ToList();
-
- var itemIdList = new List<Guid>();
- foreach (var name in names)
- {
- itemIdList.AddRange(libraryManager.GetItemIds(new InternalItemsQuery
- {
- Person = name
- }));
- }
- query.ItemIdsFromPersonFilters = itemIdList;
- }
-
- // Apply person filter
- else if (!string.IsNullOrWhiteSpace(query.Person))
- {
- var itemIdList = new List<Guid>();
-
- itemIdList.AddRange(libraryManager.GetItemIds(new InternalItemsQuery
- {
- Person = query.Person,
- PersonTypes = query.PersonTypes
- }));
- query.ItemIdsFromPersonFilters = itemIdList;
- }
- }
-
- if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
- {
- return false;
- }
-
- if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
- {
- return false;
- }
-
- UserItemData userData = null;
-
- if (query.IsLiked.HasValue)
- {
- userData = userData ?? userDataManager.GetUserData(user, item);
-
- if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
- {
- return false;
- }
- }
-
- if (query.IsFavoriteOrLiked.HasValue)
- {
- userData = userData ?? userDataManager.GetUserData(user, item);
- var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
-
- if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
- {
- return false;
- }
- }
-
- if (query.IsFavorite.HasValue)
- {
- userData = userData ?? userDataManager.GetUserData(user, item);
-
- if (userData.IsFavorite != query.IsFavorite.Value)
- {
- return false;
- }
- }
-
- if (query.IsResumable.HasValue)
- {
- userData = userData ?? userDataManager.GetUserData(user, item);
- var isResumable = userData.PlaybackPositionTicks > 0;
-
- if (isResumable != query.IsResumable.Value)
- {
- return false;
- }
- }
-
- if (query.IsPlayed.HasValue)
- {
- if (item.IsPlayed(user) != query.IsPlayed.Value)
- {
- return false;
- }
- }
-
- if (query.IsInBoxSet.HasValue)
- {
- var val = query.IsInBoxSet.Value;
- if (item.GetParents().OfType<BoxSet>().Any() != val)
- {
- return false;
- }
- }
-
- // Filter by Video3DFormat
- if (query.Is3D.HasValue)
- {
- var val = query.Is3D.Value;
- var video = item as Video;
-
- if (video == null || val != video.Video3DFormat.HasValue)
- {
- return false;
- }
- }
-
- if (query.IsHD.HasValue)
- {
- var val = query.IsHD.Value;
- var video = item as Video;
-
- if (video == null || !video.IsHD.HasValue || val != video.IsHD)
- {
- return false;
- }
- }
-
- if (query.IsLocked.HasValue)
- {
- var val = query.IsLocked.Value;
- if (item.IsLocked != val)
- {
- return false;
- }
- }
-
- if (query.HasOverview.HasValue)
- {
- var filterValue = query.HasOverview.Value;
-
- var hasValue = !string.IsNullOrEmpty(item.Overview);
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (query.HasImdbId.HasValue)
- {
- var filterValue = query.HasImdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Imdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (query.HasTmdbId.HasValue)
- {
- var filterValue = query.HasTmdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (query.HasTvdbId.HasValue)
- {
- var filterValue = query.HasTvdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tvdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (query.HasOfficialRating.HasValue)
- {
- var filterValue = query.HasOfficialRating.Value;
-
- var hasValue = !string.IsNullOrEmpty(item.OfficialRating);
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (query.IsPlaceHolder.HasValue)
- {
- var filterValue = query.IsPlaceHolder.Value;
-
- var isPlaceHolder = false;
-
- var hasPlaceHolder = item as ISupportsPlaceHolders;
-
- if (hasPlaceHolder != null)
- {
- isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
- }
-
- if (isPlaceHolder != filterValue)
- {
- return false;
- }
- }
-
- if (query.HasSpecialFeature.HasValue)
- {
- var filterValue = query.HasSpecialFeature.Value;
-
- var movie = item as IHasSpecialFeatures;
-
- if (movie != null)
- {
- var ok = filterValue
- ? movie.SpecialFeatureIds.Length > 0
- : movie.SpecialFeatureIds.Length == 0;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (query.HasSubtitles.HasValue)
- {
- var val = query.HasSubtitles.Value;
-
- var video = item as Video;
-
- if (video == null || val != video.HasSubtitles)
- {
- return false;
- }
- }
-
- if (query.HasParentalRating.HasValue)
- {
- var val = query.HasParentalRating.Value;
-
- var rating = item.CustomRating;
-
- if (string.IsNullOrEmpty(rating))
- {
- rating = item.OfficialRating;
- }
-
- if (val)
- {
- if (string.IsNullOrEmpty(rating))
- {
- return false;
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(rating))
- {
- return false;
- }
- }
- }
-
- if (query.HasTrailer.HasValue)
- {
- var val = query.HasTrailer.Value;
- var trailerCount = 0;
-
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- trailerCount = hasTrailers.GetTrailerIds().Count;
- }
-
- var ok = val ? trailerCount > 0 : trailerCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- if (query.HasThemeSong.HasValue)
- {
- var filterValue = query.HasThemeSong.Value;
-
- var themeCount = item.ThemeSongIds.Length;
- var ok = filterValue ? themeCount > 0 : themeCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- if (query.HasThemeVideo.HasValue)
- {
- var filterValue = query.HasThemeVideo.Value;
-
- var themeCount = item.ThemeVideoIds.Length;
- var ok = filterValue ? themeCount > 0 : themeCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- // Apply genre filter
- if (query.Genres.Length > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
-
- // Filter by VideoType
- if (query.VideoTypes.Length > 0)
- {
- var video = item as Video;
- if (video == null || !query.VideoTypes.Contains(video.VideoType))
- {
- return false;
- }
- }
-
- if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
- {
- return false;
- }
-
- // Apply studio filter
- if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
- {
- var studioItem = libraryManager.GetItemById(id);
- return studioItem != null && item.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase);
- }))
- {
- return false;
- }
-
- // Apply genre filter
- if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
- {
- var genreItem = libraryManager.GetItemById(id);
- return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
- }))
- {
- return false;
- }
-
- // Apply year filter
- if (query.Years.Length > 0)
- {
- if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value)))
- {
- return false;
- }
- }
-
- // Apply official rating filter
- if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty))
- {
- return false;
- }
-
- if (query.ItemIds.Length > 0)
- {
- if (!query.ItemIds.Contains(item.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- // Apply person filter
- if (query.ItemIdsFromPersonFilters != null)
- {
- if (!query.ItemIdsFromPersonFilters.Contains(item.Id))
- {
- return false;
- }
- }
-
- // Apply tag filter
- var tags = query.Tags;
- if (tags.Length > 0)
- {
- if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- if (query.MinPlayers.HasValue)
- {
- var filterValue = query.MinPlayers.Value;
-
- var game = item as Game;
-
- if (game != null)
- {
- var players = game.PlayersSupported ?? 1;
-
- var ok = players >= filterValue;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (query.MaxPlayers.HasValue)
- {
- var filterValue = query.MaxPlayers.Value;
-
- var game = item as Game;
-
- if (game != null)
- {
- var players = game.PlayersSupported ?? 1;
-
- var ok = players <= filterValue;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (query.MinCommunityRating.HasValue)
- {
- var val = query.MinCommunityRating.Value;
-
- if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
- {
- return false;
- }
- }
-
- if (query.MinCriticRating.HasValue)
- {
- var val = query.MinCriticRating.Value;
-
- if (!(item.CriticRating.HasValue && item.CriticRating >= val))
- {
- return false;
- }
- }
-
- if (query.MinIndexNumber.HasValue)
- {
- var val = query.MinIndexNumber.Value;
-
- if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
- {
- return false;
- }
- }
-
- if (query.MinPremiereDate.HasValue)
- {
- var val = query.MinPremiereDate.Value;
-
- if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
- {
- return false;
- }
- }
-
- if (query.MaxPremiereDate.HasValue)
- {
- var val = query.MaxPremiereDate.Value;
-
- if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
- {
- return false;
- }
- }
-
- if (query.ParentIndexNumber.HasValue)
- {
- var filterValue = query.ParentIndexNumber.Value;
-
- if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
- {
- return false;
- }
- }
-
- if (query.SeriesStatuses.Length > 0)
- {
- var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value));
- if (!ok)
- {
- return false;
- }
- }
-
- if (query.AiredDuringSeason.HasValue)
- {
- var episode = item as Episode;
-
- if (episode == null)
- {
- return false;
- }
-
- if (!Series.FilterEpisodesBySeason(new[] { episode }, query.AiredDuringSeason.Value, true).Any())
- {
- return false;
- }
- }
-
- return true;
- }
-
- private IEnumerable<BaseItem> GetMediaFolders(User user)
- {
- if (user == null)
- {
- return _libraryManager.RootFolder
- .Children
- .OfType<Folder>()
- .Where(UserView.IsEligibleForGrouping);
- }
- return user.RootFolder
- .GetChildren(user, true)
- .OfType<Folder>()
- .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
- }
-
- private List<BaseItem> GetMediaFolders(User user, IEnumerable<string> viewTypes)
- {
- if (user == null)
- {
- return GetMediaFolders(null)
- .Where(i =>
- {
- var folder = i as ICollectionFolder;
-
- return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- }).ToList();
- }
- return GetMediaFolders(user)
- .Where(i =>
- {
- var folder = i as ICollectionFolder;
-
- return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- }).ToList();
- }
-
- private List<BaseItem> GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
- {
- if (parent == null || parent is UserView)
- {
- return GetMediaFolders(user, viewTypes);
- }
-
- return new List<BaseItem> { parent };
- }
-
- private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query)
- {
- if (query.Recursive)
- {
- return await _liveTvManager.GetInternalRecordings(new RecordingQuery
- {
- IsInProgress = false,
- Status = RecordingStatus.Completed,
- UserId = user.Id.ToString("N")
-
- }, new DtoOptions(), CancellationToken.None).ConfigureAwait(false);
- }
-
- var list = new List<BaseItem>();
-
- //list.Add(await GetUserSubView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false));
- list.Add(GetUserView(SpecialFolder.LiveTvChannels, "Channels", string.Empty, user.RootFolder));
- list.Add(GetUserView(SpecialFolder.LiveTvRecordingGroups, "HeaderRecordingGroups", string.Empty, user.RootFolder));
-
- return GetResult(list, queryParent, query);
- }
-
- private UserView GetUserViewWithName(string name, string type, string sortName, BaseItem parent)
- {
- return _userViewManager.GetUserSubView(name, parent.Id.ToString("N"), type, sortName, CancellationToken.None);
- }
-
- private UserView GetUserView(string type, string localizationKey, string sortName, BaseItem parent)
- {
- return _userViewManager.GetUserSubView(parent.Id.ToString("N"), type, localizationKey, sortName, CancellationToken.None);
- }
-
- public static IEnumerable<BaseItem> FilterForAdjacency(List<BaseItem> list, string adjacentToId)
- {
- var adjacentToIdGuid = new Guid(adjacentToId);
- var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid);
-
- var index = list.IndexOf(adjacentToItem);
-
- var previousId = Guid.Empty;
- var nextId = Guid.Empty;
-
- if (index > 0)
- {
- previousId = list[index - 1].Id;
- }
-
- if (index < list.Count - 1)
- {
- nextId = list[index + 1].Id;
- }
-
- return list.Where(i => i.Id == previousId || i.Id == nextId || i.Id == adjacentToIdGuid);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
deleted file mode 100644
index dca1cfd01..000000000
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ /dev/null
@@ -1,806 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Video
- /// </summary>
- public class Video : BaseItem,
- IHasAspectRatio,
- ISupportsPlaceHolders,
- IHasMediaSources
- {
- [IgnoreDataMember]
- public string PrimaryVersionId { get; set; }
-
- public string[] AdditionalParts { get; set; }
- public string[] LocalAlternateVersions { get; set; }
- public LinkedChild[] LinkedAlternateVersions { get; set; }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPositionTicksResume
- {
- get
- {
- var extraType = ExtraType;
- if (extraType.HasValue)
- {
- if (extraType.Value == Model.Entities.ExtraType.Sample)
- {
- return false;
- }
- if (extraType.Value == Model.Entities.ExtraType.ThemeVideo)
- {
- return false;
- }
- if (extraType.Value == Model.Entities.ExtraType.Trailer)
- {
- return false;
- }
- }
- return true;
- }
- }
-
- public void SetPrimaryVersionId(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- PrimaryVersionId = null;
- }
- else
- {
- PrimaryVersionId = id;
- }
-
- PresentationUniqueKey = CreatePresentationUniqueKey();
- }
-
- public override string CreatePresentationUniqueKey()
- {
- if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
- {
- return PrimaryVersionId;
- }
-
- return base.CreatePresentationUniqueKey();
- }
-
- [IgnoreDataMember]
- public override bool EnableRefreshOnDateModifiedChange
- {
- get
- {
- return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsThemeMedia
- {
- get { return true; }
- }
-
- /// <summary>
- /// Gets or sets the timestamp.
- /// </summary>
- /// <value>The timestamp.</value>
- public TransportStreamTimestamp? Timestamp { get; set; }
-
- /// <summary>
- /// Gets or sets the subtitle paths.
- /// </summary>
- /// <value>The subtitle paths.</value>
- public string[] SubtitleFiles { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has subtitles.
- /// </summary>
- /// <value><c>true</c> if this instance has subtitles; otherwise, <c>false</c>.</value>
- public bool HasSubtitles { get; set; }
-
- public bool IsPlaceHolder { get; set; }
- public bool IsShortcut { get; set; }
- public string ShortcutPath { get; set; }
-
- /// <summary>
- /// Gets or sets the default index of the video stream.
- /// </summary>
- /// <value>The default index of the video stream.</value>
- public int? DefaultVideoStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the video.
- /// </summary>
- /// <value>The type of the video.</value>
- public VideoType VideoType { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the iso.
- /// </summary>
- /// <value>The type of the iso.</value>
- public IsoType? IsoType { get; set; }
-
- /// <summary>
- /// Gets or sets the video3 D format.
- /// </summary>
- /// <value>The video3 D format.</value>
- public Video3DFormat? Video3DFormat { get; set; }
-
- public string[] GetPlayableStreamFileNames()
- {
- 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;
- }
- else
- {
- return new string[] { };
- }
- return MediaEncoder.GetPlayableStreamFileNames(Path, videoType);
- }
-
- /// <summary>
- /// Gets or sets the aspect ratio.
- /// </summary>
- /// <value>The aspect ratio.</value>
- public string AspectRatio { get; set; }
-
- public Video()
- {
- AdditionalParts = EmptyStringArray;
- LocalAlternateVersions = EmptyStringArray;
- SubtitleFiles = EmptyStringArray;
- LinkedAlternateVersions = EmptyLinkedChildArray;
- }
-
- public override bool CanDownload()
- {
- if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
- {
- return false;
- }
-
- var locationType = LocationType;
- return locationType != LocationType.Remote &&
- locationType != LocationType.Virtual;
- }
-
- [IgnoreDataMember]
- public override bool SupportsAddingToPlaylist
- {
- get { return true; }
- }
-
- [IgnoreDataMember]
- public int MediaSourceCount
- {
- get
- {
- if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
- {
- var item = LibraryManager.GetItemById(PrimaryVersionId) as Video;
- if (item != null)
- {
- return item.MediaSourceCount;
- }
- }
- return LinkedAlternateVersions.Length + LocalAlternateVersions.Length + 1;
- }
- }
-
- [IgnoreDataMember]
- public bool IsStacked
- {
- get { return AdditionalParts.Length > 0; }
- }
-
- [IgnoreDataMember]
- public bool HasLocalAlternateVersions
- {
- get { return LocalAlternateVersions.Length > 0; }
- }
-
- public IEnumerable<Guid> GetAdditionalPartIds()
- {
- return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
- }
-
- public IEnumerable<Guid> GetLocalAlternateVersionIds()
- {
- return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
- }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get
- {
- if (IsActiveRecording())
- {
- return SourceType.LiveTV;
- }
-
- return base.SourceType;
- }
- }
-
- protected bool IsActiveRecording()
- {
- return LiveTvManager.GetActiveRecordingInfo(Path) != null;
- }
-
- public override bool CanDelete()
- {
- if (IsActiveRecording())
- {
- return false;
- }
-
- return base.CanDelete();
- }
-
- [IgnoreDataMember]
- public bool IsCompleteMedia
- {
- get { return !IsActiveRecording(); }
- }
-
- [IgnoreDataMember]
- protected virtual bool EnableDefaultVideoUserDataKeys
- {
- get
- {
- return true;
- }
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- if (EnableDefaultVideoUserDataKeys)
- {
- if (ExtraType.HasValue)
- {
- var key = this.GetProviderId(MetadataProviders.Tmdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, GetUserDataKey(key));
- }
-
- key = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, GetUserDataKey(key));
- }
- }
- else
- {
- var key = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
-
- key = this.GetProviderId(MetadataProviders.Tmdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
- }
- }
-
- return list;
- }
-
- private string GetUserDataKey(string providerId)
- {
- var key = providerId + "-" + ExtraType.ToString().ToLower();
-
- // Make sure different trailers have their own data.
- if (RunTimeTicks.HasValue)
- {
- key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- return key;
- }
-
- public IEnumerable<Video> GetLinkedAlternateVersions()
- {
- return LinkedAlternateVersions
- .Select(GetLinkedChild)
- .Where(i => i != null)
- .OfType<Video>()
- .OrderBy(i => i.SortName);
- }
-
- /// <summary>
- /// Gets the additional parts.
- /// </summary>
- /// <returns>IEnumerable{Video}.</returns>
- public IEnumerable<Video> GetAdditionalParts()
- {
- return GetAdditionalPartIds()
- .Select(i => LibraryManager.GetItemById(i))
- .Where(i => i != null)
- .OfType<Video>()
- .OrderBy(i => i.SortName);
- }
-
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- if (IsStacked)
- {
- return FileSystem.GetDirectoryName(Path);
- }
-
- if (!IsPlaceHolder)
- {
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
- {
- return Path;
- }
- }
-
- return base.ContainingFolderPath;
- }
- }
-
- [IgnoreDataMember]
- public override string FileNameWithoutExtension
- {
- get
- {
- if (LocationType == LocationType.FileSystem)
- {
- if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
- {
- return System.IO.Path.GetFileName(Path);
- }
-
- return System.IO.Path.GetFileNameWithoutExtension(Path);
- }
-
- return null;
- }
- }
-
- internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
- {
- var updateType = base.UpdateFromResolvedItem(newItem);
-
- var newVideo = newItem as Video;
- if (newVideo != null)
- {
- if (!AdditionalParts.SequenceEqual(newVideo.AdditionalParts, StringComparer.Ordinal))
- {
- AdditionalParts = newVideo.AdditionalParts;
- updateType |= ItemUpdateType.MetadataImport;
- }
- if (!LocalAlternateVersions.SequenceEqual(newVideo.LocalAlternateVersions, StringComparer.Ordinal))
- {
- LocalAlternateVersions = newVideo.LocalAlternateVersions;
- updateType |= ItemUpdateType.MetadataImport;
- }
- if (VideoType != newVideo.VideoType)
- {
- VideoType = newVideo.VideoType;
- updateType |= ItemUpdateType.MetadataImport;
- }
- }
-
- return updateType;
- }
-
- public static 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)
- .ToArray();
- }
- 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)
- .ToArray();
- }
- return new string[] { };
- }
-
- /// <summary>
- /// Gets a value indicating whether [is3 D].
- /// </summary>
- /// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool Is3D
- {
- get { return Video3DFormat.HasValue; }
- }
-
- /// <summary>
- /// Gets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Video;
- }
- }
-
- protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
- {
- var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
-
- if (IsStacked)
- {
- var tasks = AdditionalParts
- .Select(i => RefreshMetadataForOwnedVideo(options, true, i, cancellationToken));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- // Must have a parent to have additional parts or alternate versions
- // In other words, it must be part of the Parent/Child tree
- // The additional parts won't have additional parts themselves
- if (LocationType == LocationType.FileSystem && GetParent() != null)
- {
- if (!IsStacked)
- {
- RefreshLinkedAlternateVersions();
-
- var tasks = LocalAlternateVersions
- .Select(i => RefreshMetadataForOwnedVideo(options, false, i, cancellationToken));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
- }
-
- return hasChanges;
- }
-
- private void RefreshLinkedAlternateVersions()
- {
- foreach (var child in LinkedAlternateVersions)
- {
- // Reset the cached value
- if (child.ItemId.HasValue && child.ItemId.Value == Guid.Empty)
- {
- child.ItemId = null;
- }
- }
- }
-
- public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- base.UpdateToRepository(updateReason, cancellationToken);
-
- var localAlternates = GetLocalAlternateVersionIds()
- .Select(i => LibraryManager.GetItemById(i))
- .Where(i => i != null);
-
- foreach (var item in localAlternates)
- {
- item.ImageInfos = ImageInfos;
- item.Overview = Overview;
- item.ProductionYear = ProductionYear;
- item.PremiereDate = PremiereDate;
- item.CommunityRating = CommunityRating;
- item.OfficialRating = OfficialRating;
- item.Genres = Genres;
- item.ProviderIds = ProviderIds;
-
- item.UpdateToRepository(ItemUpdateType.MetadataDownload, cancellationToken);
- }
- }
-
- public override IEnumerable<FileSystemMetadata> GetDeletePaths()
- {
- if (!IsInMixedFolder)
- {
- return new[] {
- new FileSystemMetadata
- {
- FullName = ContainingFolderPath,
- IsDirectory = true
- }
- };
- }
-
- return base.GetDeletePaths();
- }
-
- public List<MediaStream> GetMediaStreams()
- {
- return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
- {
- ItemId = Id
- });
- }
-
- public virtual MediaStream GetDefaultVideoStream()
- {
- if (!DefaultVideoStreamIndex.HasValue)
- {
- return null;
- }
-
- return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
- {
- ItemId = Id,
- Index = DefaultVideoStreamIndex.Value
-
- }).FirstOrDefault();
- }
-
- private List<Tuple<Video, MediaSourceType>> GetAllVideosForMediaSources()
- {
- var list = new List<Tuple<Video, MediaSourceType>>();
-
- list.Add(new Tuple<Video, MediaSourceType>(this, MediaSourceType.Default));
- list.AddRange(GetLinkedAlternateVersions().Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
-
- if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
- {
- var primary = LibraryManager.GetItemById(PrimaryVersionId) as Video;
- if (primary != null)
- {
- var existingIds = list.Select(i => i.Item1.Id).ToList();
- list.Add(new Tuple<Video, MediaSourceType>(primary, MediaSourceType.Grouping));
- list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
- }
- }
-
- var localAlternates = list
- .SelectMany(i => i.Item1.GetLocalAlternateVersionIds())
- .Select(LibraryManager.GetItemById)
- .Where(i => i != null)
- .OfType<Video>()
- .ToList();
-
- list.AddRange(localAlternates.Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Default)));
-
- return list;
- }
-
- public virtual List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
- {
- if (SourceType == SourceType.Channel)
- {
- var sources = ChannelManager.GetStaticMediaSources(this, CancellationToken.None)
- .ToList();
-
- if (sources.Count > 0)
- {
- return sources;
- }
-
- return new List<MediaSourceInfo>
- {
- GetVersionInfo(enablePathSubstitution, this, MediaSourceType.Placeholder)
- };
- }
-
- var list = GetAllVideosForMediaSources();
- var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
-
- if (IsActiveRecording())
- {
- foreach (var mediaSource in result)
- {
- mediaSource.Type = MediaSourceType.Placeholder;
- }
- }
-
- return result.OrderBy(i =>
- {
- if (i.VideoType == VideoType.VideoFile)
- {
- return 0;
- }
-
- return 1;
-
- }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
- .ThenByDescending(i =>
- {
- var stream = i.VideoStream;
-
- return stream == null || stream.Width == null ? 0 : stream.Width.Value;
- })
- .ToList();
- }
-
- private static MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, Video media, MediaSourceType type)
- {
- if (media == null)
- {
- throw new ArgumentNullException("media");
- }
-
- var locationType = media.LocationType;
-
- var info = new MediaSourceInfo
- {
- Id = media.Id.ToString("N"),
- IsoType = media.IsoType,
- Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
- MediaStreams = MediaSourceManager.GetMediaStreams(media.Id),
- Name = GetMediaSourceName(media),
- Path = enablePathSubstitution ? GetMappedPath(media, media.Path, locationType) : media.Path,
- RunTimeTicks = media.RunTimeTicks,
- Video3DFormat = media.Video3DFormat,
- VideoType = media.VideoType,
- Container = media.Container,
- Size = media.Size,
- Timestamp = media.Timestamp,
- Type = type,
- SupportsDirectStream = media.VideoType == VideoType.VideoFile,
- IsRemote = media.IsShortcut
- };
-
- if (info.Protocol == MediaProtocol.File)
- {
- info.ETag = media.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N");
- }
-
- if (media.IsShortcut)
- {
- info.Path = media.ShortcutPath;
-
- if (!string.IsNullOrWhiteSpace(info.Path))
- {
- if (info.Path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
- {
- info.Protocol = MediaProtocol.Http;
- info.SupportsDirectStream = false;
- }
- else if (info.Path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
- {
- info.Protocol = MediaProtocol.Rtmp;
- info.SupportsDirectStream = false;
- }
- else if (info.Path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
- {
- info.Protocol = MediaProtocol.Rtsp;
- info.SupportsDirectStream = false;
- }
- else
- {
- info.Protocol = MediaProtocol.File;
- }
- }
- }
-
- if (string.IsNullOrEmpty(info.Container))
- {
- if (media.VideoType == VideoType.VideoFile || media.VideoType == VideoType.Iso)
- {
- if (!string.IsNullOrWhiteSpace(media.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
- {
- info.Container = System.IO.Path.GetExtension(media.Path).TrimStart('.');
- }
- }
- }
-
- info.Bitrate = media.TotalBitrate;
- info.InferTotalBitrate();
-
- return info;
- }
-
- private static string GetMediaSourceName(Video video)
- {
- var terms = new List<string>();
-
- var locationType = video.LocationType;
- var path = video.Path;
- if ((locationType == LocationType.FileSystem || locationType == LocationType.Offline) && !string.IsNullOrWhiteSpace(path))
- {
- terms.Add(System.IO.Path.GetFileName(path));
- }
- else
- {
- terms.Add(video.Name);
- }
-
- if (video.Video3DFormat.HasValue)
- {
- terms.Add("3D");
- }
-
- if (video.VideoType == VideoType.BluRay)
- {
- terms.Add("Bluray");
- }
- else if (video.VideoType == VideoType.Dvd)
- {
- terms.Add("DVD");
- }
- else if (video.VideoType == VideoType.Iso)
- {
- if (video.IsoType.HasValue)
- {
- if (video.IsoType.Value == Model.Entities.IsoType.BluRay)
- {
- terms.Add("Bluray");
- }
- else if (video.IsoType.Value == Model.Entities.IsoType.Dvd)
- {
- terms.Add("DVD");
- }
- }
- else
- {
- terms.Add("ISO");
- }
- }
-
- return string.Join("/", terms.ToArray(terms.Count));
- }
-
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
deleted file mode 100644
index 49b967104..000000000
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class Year
- /// </summary>
- public class Year : BaseItem, IItemByName
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- list.Insert(0, "Year-" + Name);
- return list;
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
- {
- int year;
-
- var usCulture = new CultureInfo("en-US");
-
- if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year))
- {
- return new List<BaseItem>();
- }
-
- query.Years = new[] { year };
-
- return LibraryManager.GetItemList(query);
- }
-
- public int? GetYearValue()
- {
- int i;
-
- if (int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
- {
- return i;
- }
-
- return null;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- return false;
- }
- }
-
- public static string GetPath(string name)
- {
- return GetPath(name, true);
- }
-
- public static string GetPath(string name, bool normalizeName)
- {
- // Trim the period at the end because windows will have a hard time with that
- var validName = normalizeName ?
- FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
- name;
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.YearPath, validName);
- }
-
- private string GetRebasedPath()
- {
- return GetPath(System.IO.Path.GetFileName(Path), false);
- }
-
- public override bool RequiresRefresh()
- {
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
- return true;
- }
- return base.RequiresRefresh();
- }
-
- /// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
- /// </summary>
- public override bool BeforeMetadataRefresh()
- {
- var hasChanges = base.BeforeMetadataRefresh();
-
- var newPath = GetRebasedPath();
- if (!string.Equals(Path, newPath, StringComparison.Ordinal))
- {
- Path = newPath;
- hasChanges = true;
- }
-
- return hasChanges;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs
deleted file mode 100644
index 60e7815db..000000000
--- a/MediaBrowser.Controller/Extensions/StringExtensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Controller.Extensions
-{
- /// <summary>
- /// Class BaseExtensions
- /// </summary>
- public static class StringExtensions
- {
- public static ILocalizationManager LocalizationManager { get; set; }
-
- public static string RemoveDiacritics(this string text)
- {
- return LocalizationManager.RemoveDiacritics(text);
- }
- }
-}
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
deleted file mode 100644
index 880b3a0ce..000000000
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.IO
-{
- /// <summary>
- /// Provides low level File access that is much faster than the File/Directory api's
- /// </summary>
- public static class FileData
- {
- private static Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(FileSystemMetadata[] list)
- {
- var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var file in list)
- {
- dict[file.FullName] = file;
- }
- return dict;
- }
-
- /// <summary>
- /// Gets the filtered file system entries.
- /// </summary>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="path">The path.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="args">The args.</param>
- /// <param name="flattenFolderDepth">The flatten folder depth.</param>
- /// <param name="resolveShortcuts">if set to <c>true</c> [resolve shortcuts].</param>
- /// <returns>Dictionary{System.StringFileSystemInfo}.</returns>
- /// <exception cref="System.ArgumentNullException">path</exception>
- public static FileSystemMetadata[] GetFilteredFileSystemEntries(IDirectoryService directoryService,
- string path,
- IFileSystem fileSystem,
- ILogger logger,
- ItemResolveArgs args,
- int flattenFolderDepth = 0,
- bool resolveShortcuts = true)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
- if (args == null)
- {
- throw new ArgumentNullException("args");
- }
-
- var entries = directoryService.GetFileSystemEntries(path);
-
- if (!resolveShortcuts && flattenFolderDepth == 0)
- {
- return entries;
- }
-
- var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var entry in entries)
- {
- var isDirectory = entry.IsDirectory;
-
- var fullName = entry.FullName;
-
- if (resolveShortcuts && fileSystem.IsShortcut(fullName))
- {
- try
- {
- var newPath = fileSystem.ResolveShortcut(fullName);
-
- if (string.IsNullOrWhiteSpace(newPath))
- {
- //invalid shortcut - could be old or target could just be unavailable
- logger.Warn("Encountered invalid shortcut: " + fullName);
- continue;
- }
-
- // Don't check if it exists here because that could return false for network shares.
- var data = fileSystem.GetDirectoryInfo(newPath);
-
- // add to our physical locations
- args.AddAdditionalLocation(newPath);
-
- dict[newPath] = data;
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error resolving shortcut from {0}", ex, fullName);
- }
- }
- else if (flattenFolderDepth > 0 && isDirectory)
- {
- foreach (var child in GetFilteredFileSystemEntries(directoryService, fullName, fileSystem, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
- {
- dict[child.FullName] = child;
- }
- }
- else
- {
- dict[fullName] = entry;
- }
- }
-
- var returnResult = new FileSystemMetadata[dict.Count];
- var index = 0;
- var values = dict.Values;
- foreach (var value in values)
- {
- returnResult[index] = value;
- index++;
- }
- return returnResult;
- }
-
- }
-
-}
diff --git a/MediaBrowser.Controller/IO/StreamHelper.cs b/MediaBrowser.Controller/IO/StreamHelper.cs
deleted file mode 100644
index 5aec9a182..000000000
--- a/MediaBrowser.Controller/IO/StreamHelper.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.IO;
-using System.Threading;
-using System;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.IO
-{
- public static class StreamHelper
- {
- public static void CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
- {
- byte[] buffer = new byte[bufferSize];
- int read;
- while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- destination.Write(buffer, 0, read);
-
- if (onStarted != null)
- {
- onStarted();
- onStarted = null;
- }
- }
- }
-
- public static async Task CopyToAsync(Stream source, Stream destination, int bufferSize, IProgress<double> progress, long contentLength, CancellationToken cancellationToken)
- {
- byte[] buffer = new byte[bufferSize];
- int read;
- long totalRead = 0;
-
- while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- destination.Write(buffer, 0, read);
-
- totalRead += read;
-
- double pct = totalRead;
- pct /= contentLength;
- pct *= 100;
-
- progress.Report(pct);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
deleted file mode 100644
index 3f7f8248b..000000000
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Model.System;
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Net;
-using System.Threading;
-
-namespace MediaBrowser.Controller
-{
- /// <summary>
- /// Interface IServerApplicationHost
- /// </summary>
- public interface IServerApplicationHost : IApplicationHost
- {
- event EventHandler HasUpdateAvailableChanged;
-
- /// <summary>
- /// Gets the system info.
- /// </summary>
- /// <returns>SystemInfo.</returns>
- Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken);
-
- Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets a value indicating whether [supports automatic run at startup].
- /// </summary>
- /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value>
- bool SupportsAutoRunAtStartup { get; }
-
- bool CanLaunchWebBrowser { get; }
-
- /// <summary>
- /// Gets the HTTP server port.
- /// </summary>
- /// <value>The HTTP server port.</value>
- int HttpPort { get; }
-
- /// <summary>
- /// Gets the HTTPS port.
- /// </summary>
- /// <value>The HTTPS port.</value>
- int HttpsPort { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports HTTPS].
- /// </summary>
- /// <value><c>true</c> if [supports HTTPS]; otherwise, <c>false</c>.</value>
- bool EnableHttps { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- bool HasUpdateAvailable { get; }
-
- /// <summary>
- /// Gets the name of the friendly.
- /// </summary>
- /// <value>The name of the friendly.</value>
- string FriendlyName { get; }
-
- /// <summary>
- /// Gets the local ip address.
- /// </summary>
- /// <value>The local ip address.</value>
- Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the local API URL.
- /// </summary>
- /// <value>The local API URL.</value>
- Task<string> GetLocalApiUrl(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the local API URL.
- /// </summary>
- /// <param name="host">The host.</param>
- /// <returns>System.String.</returns>
- string GetLocalApiUrl(string host);
-
- /// <summary>
- /// Gets the local API URL.
- /// </summary>
- string GetLocalApiUrl(IpAddressInfo address);
-
- void LaunchUrl(string url);
-
- void EnableLoopback(string appName);
-
- string PackageRuntime { get; }
- }
-}
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
deleted file mode 100644
index 4ad1cf49a..000000000
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using MediaBrowser.Common.Configuration;
-
-namespace MediaBrowser.Controller
-{
- public interface IServerApplicationPaths : IApplicationPaths
- {
- /// <summary>
- /// Gets the path to the base root media directory
- /// </summary>
- /// <value>The root folder path.</value>
- string RootFolderPath { get; }
-
- /// <summary>
- /// Gets the application resources path. This is the path to the folder containing resources that are deployed as part of the application
- /// For example, this folder contains dashboard-ui and swagger-ui
- /// </summary>
- /// <value>The application resources path.</value>
- string ApplicationResourcesPath { get; }
-
- /// <summary>
- /// Gets the path to the default user view directory. Used if no specific user view is defined.
- /// </summary>
- /// <value>The default user views path.</value>
- string DefaultUserViewsPath { get; }
-
- /// <summary>
- /// Gets the path to localization data.
- /// </summary>
- /// <value>The localization path.</value>
- string LocalizationPath { get; }
-
- /// <summary>
- /// Gets the path to the Images By Name directory
- /// </summary>
- /// <value>The images by name path.</value>
- string ItemsByNamePath { get; }
-
- /// <summary>
- /// Gets the path to the People directory
- /// </summary>
- /// <value>The people path.</value>
- string PeoplePath { get; }
-
- /// <summary>
- /// Gets the path to the Genre directory
- /// </summary>
- /// <value>The genre path.</value>
- string GenrePath { get; }
-
- /// <summary>
- /// Gets the music genre path.
- /// </summary>
- /// <value>The music genre path.</value>
- string MusicGenrePath { get; }
-
- /// <summary>
- /// Gets the game genre path.
- /// </summary>
- /// <value>The game genre path.</value>
- string GameGenrePath { get; }
-
- /// <summary>
- /// Gets the path to the Studio directory
- /// </summary>
- /// <value>The studio path.</value>
- string StudioPath { get; }
-
- /// <summary>
- /// Gets the path to the Year directory
- /// </summary>
- /// <value>The year path.</value>
- string YearPath { get; }
-
- /// <summary>
- /// Gets the path to the General IBN directory
- /// </summary>
- /// <value>The general path.</value>
- string GeneralPath { get; }
-
- /// <summary>
- /// Gets the path to the Ratings IBN directory
- /// </summary>
- /// <value>The ratings path.</value>
- string RatingsPath { get; }
-
- /// <summary>
- /// Gets the media info images path.
- /// </summary>
- /// <value>The media info images path.</value>
- string MediaInfoImagesPath { get; }
-
- /// <summary>
- /// Gets the path to the user configuration directory
- /// </summary>
- /// <value>The user configuration directory path.</value>
- string UserConfigurationDirectoryPath { get; }
-
- /// <summary>
- /// Gets the transcoding temporary path.
- /// </summary>
- /// <value>The transcoding temporary path.</value>
- string TranscodingTempPath { get; }
-
- /// <summary>
- /// Gets the internal metadata path.
- /// </summary>
- /// <value>The internal metadata path.</value>
- string InternalMetadataPath { get; }
-
- string ArtistsPath { get; }
-
- string GetTranscodingTempPath();
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/DeleteOptions.cs b/MediaBrowser.Controller/Library/DeleteOptions.cs
deleted file mode 100644
index 81ed90899..000000000
--- a/MediaBrowser.Controller/Library/DeleteOptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Controller.Library
-{
- public class DeleteOptions
- {
- public bool DeleteFileLocation { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs
deleted file mode 100644
index 611aab387..000000000
--- a/MediaBrowser.Controller/Library/IIntroProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class BaseIntroProvider
- /// </summary>
- public interface IIntroProvider
- {
- /// <summary>
- /// Gets the intros.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{System.String}.</returns>
- Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user);
-
- /// <summary>
- /// Gets all intro files.
- /// </summary>
- /// <returns>IEnumerable{System.String}.</returns>
- IEnumerable<string> GetAllIntroFiles();
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
deleted file mode 100644
index 9ef372eb6..000000000
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ /dev/null
@@ -1,559 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface ILibraryManager
- /// </summary>
- public interface ILibraryManager
- {
- /// <summary>
- /// Resolves the path.
- /// </summary>
- /// <param name="fileInfo">The file information.</param>
- /// <param name="parent">The parent.</param>
- /// <returns>BaseItem.</returns>
- BaseItem ResolvePath(FileSystemMetadata fileInfo,
- Folder parent = null);
-
- /// <summary>
- /// Resolves a set of files into a list of BaseItem
- /// </summary>
- IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
- IDirectoryService directoryService,
- Folder parent,
- LibraryOptions libraryOptions,
- string collectionType = null);
-
- /// <summary>
- /// Gets the root folder.
- /// </summary>
- /// <value>The root folder.</value>
- AggregateFolder RootFolder { get; }
-
- /// <summary>
- /// Gets a Person
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Person}.</returns>
- Person GetPerson(string name);
-
- /// <summary>
- /// Finds the by path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>BaseItem.</returns>
- BaseItem FindByPath(string path, bool? isFolder);
-
- /// <summary>
- /// Gets the artist.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Artist}.</returns>
- MusicArtist GetArtist(string name);
- MusicArtist GetArtist(string name, DtoOptions options);
- /// <summary>
- /// Gets a Studio
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Studio}.</returns>
- Studio GetStudio(string name);
-
- /// <summary>
- /// Gets a Genre
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Genre}.</returns>
- Genre GetGenre(string name);
-
- /// <summary>
- /// Gets the genre.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{MusicGenre}.</returns>
- MusicGenre GetMusicGenre(string name);
-
- /// <summary>
- /// Gets the game genre.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{GameGenre}.</returns>
- GameGenre GetGameGenre(string name);
-
- /// <summary>
- /// Gets a Year
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>Task{Year}.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException"></exception>
- Year GetYear(int value);
-
- /// <summary>
- /// Validate and refresh the People sub-set of the IBN.
- /// The items are stored in the db but not loaded into memory until actually requested by an operation.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress);
-
- /// <summary>
- /// Reloads the root media folder
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken);
-
- /// <summary>
- /// Queues the library scan.
- /// </summary>
- void QueueLibraryScan();
-
- void UpdateImages(BaseItem item);
-
- /// <summary>
- /// Gets the default view.
- /// </summary>
- /// <returns>IEnumerable{VirtualFolderInfo}.</returns>
- List<VirtualFolderInfo> GetVirtualFolders();
-
- List<VirtualFolderInfo> GetVirtualFolders(bool includeRefreshState);
-
- /// <summary>
- /// Gets the item by id.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- BaseItem GetItemById(Guid id);
-
- /// <summary>
- /// Gets the intros.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{System.String}.</returns>
- Task<IEnumerable<Video>> GetIntros(BaseItem item, User user);
-
- /// <summary>
- /// Gets all intro files.
- /// </summary>
- /// <returns>IEnumerable{System.String}.</returns>
- IEnumerable<string> GetAllIntroFiles();
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="rules">The rules.</param>
- /// <param name="pluginFolders">The plugin folders.</param>
- /// <param name="resolvers">The resolvers.</param>
- /// <param name="introProviders">The intro providers.</param>
- /// <param name="itemComparers">The item comparers.</param>
- /// <param name="postscanTasks">The postscan tasks.</param>
- void AddParts(IEnumerable<IResolverIgnoreRule> rules,
- IEnumerable<IVirtualFolderCreator> pluginFolders,
- IEnumerable<IItemResolver> resolvers,
- IEnumerable<IIntroProvider> introProviders,
- IEnumerable<IBaseItemComparer> itemComparers,
- IEnumerable<ILibraryPostScanTask> postscanTasks);
-
- /// <summary>
- /// Sorts the specified items.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="user">The user.</param>
- /// <param name="sortBy">The sort by.</param>
- /// <param name="sortOrder">The sort order.</param>
- /// <returns>IEnumerable{BaseItem}.</returns>
- IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
- IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<Tuple<string, SortOrder>> orderBy);
-
- /// <summary>
- /// Gets the user root folder.
- /// </summary>
- /// <returns>UserRootFolder.</returns>
- Folder GetUserRootFolder();
-
- /// <summary>
- /// Creates the item.
- /// </summary>
- void CreateItem(BaseItem item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the items.
- /// </summary>
- void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the item.
- /// </summary>
- void UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken);
-
- /// <summary>
- /// Retrieves the item.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- BaseItem RetrieveItem(Guid id);
-
- bool IsScanRunning { get; }
-
- /// <summary>
- /// Occurs when [item added].
- /// </summary>
- event EventHandler<ItemChangeEventArgs> ItemAdded;
-
- /// <summary>
- /// Occurs when [item updated].
- /// </summary>
- event EventHandler<ItemChangeEventArgs> ItemUpdated;
- /// <summary>
- /// Occurs when [item removed].
- /// </summary>
- event EventHandler<ItemChangeEventArgs> ItemRemoved;
-
- /// <summary>
- /// Reports the item removed.
- /// </summary>
- void ReportItemRemoved(BaseItem item, BaseItem parent);
-
- /// <summary>
- /// Finds the type of the collection.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- string GetContentType(BaseItem item);
-
- /// <summary>
- /// Gets the type of the inherited content.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- string GetInheritedContentType(BaseItem item);
-
- /// <summary>
- /// Gets the type of the configured content.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- string GetConfiguredContentType(BaseItem item);
-
- /// <summary>
- /// Gets the type of the configured content.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- string GetConfiguredContentType(string path);
-
- /// <summary>
- /// Normalizes the root path list.
- /// </summary>
- /// <param name="paths">The paths.</param>
- /// <returns>IEnumerable{System.String}.</returns>
- List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths);
-
- /// <summary>
- /// Registers the item.
- /// </summary>
- /// <param name="item">The item.</param>
- void RegisterItem(BaseItem item);
-
- /// <summary>
- /// Deletes the item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <returns>Task.</returns>
- Task DeleteItem(BaseItem item, DeleteOptions options);
-
- /// <summary>
- /// Gets the named view.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="name">The name.</param>
- /// <param name="parentId">The parent identifier.</param>
- /// <param name="viewType">Type of the view.</param>
- /// <param name="sortName">Name of the sort.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;UserView&gt;.</returns>
- UserView GetNamedView(User user,
- string name,
- string parentId,
- string viewType,
- string sortName,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the named view.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="name">The name.</param>
- /// <param name="viewType">Type of the view.</param>
- /// <param name="sortName">Name of the sort.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;UserView&gt;.</returns>
- UserView GetNamedView(User user,
- string name,
- string viewType,
- string sortName,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the named view.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="viewType">Type of the view.</param>
- /// <param name="sortName">Name of the sort.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- UserView GetNamedView(string name,
- string viewType,
- string sortName,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the named view.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="parentId">The parent identifier.</param>
- /// <param name="viewType">Type of the view.</param>
- /// <param name="sortName">Name of the sort.</param>
- /// <param name="uniqueId">The unique identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- UserView GetNamedView(string name,
- string parentId,
- string viewType,
- string sortName,
- string uniqueId,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the shadow view.
- /// </summary>
- /// <param name="parent">The parent.</param>
- /// <param name="viewType">Type of the view.</param>
- /// <param name="sortName">Name of the sort.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;UserView&gt;.</returns>
- UserView GetShadowView(BaseItem parent,
- string viewType,
- string sortName,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Determines whether [is video file] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is video file] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsVideoFile(string path);
-
- /// <summary>
- /// Determines whether [is audio file] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is audio file] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsAudioFile(string path);
-
- bool IsAudioFile(string path, LibraryOptions libraryOptions);
- bool IsVideoFile(string path, LibraryOptions libraryOptions);
-
- /// <summary>
- /// Gets the season number from path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- int? GetSeasonNumberFromPath(string path);
-
- /// <summary>
- /// Fills the missing episode numbers from path.
- /// </summary>
- /// <param name="episode">The episode.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool FillMissingEpisodeNumbersFromPath(Episode episode);
-
- /// <summary>
- /// Parses the name.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>ItemInfo.</returns>
- ItemLookupInfo ParseName(string name);
-
- /// <summary>
- /// Gets the new item identifier.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="type">The type.</param>
- /// <returns>Guid.</returns>
- Guid GetNewItemId(string key, Type type);
-
- /// <summary>
- /// Finds the trailers.
- /// </summary>
- /// <param name="owner">The owner.</param>
- /// <param name="fileSystemChildren">The file system children.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns>IEnumerable&lt;Trailer&gt;.</returns>
- IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
- IDirectoryService directoryService);
-
- /// <summary>
- /// Finds the extras.
- /// </summary>
- /// <param name="owner">The owner.</param>
- /// <param name="fileSystemChildren">The file system children.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns>IEnumerable&lt;Video&gt;.</returns>
- IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
- IDirectoryService directoryService);
-
- /// <summary>
- /// Gets the collection folders.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IEnumerable&lt;Folder&gt;.</returns>
- List<Folder> GetCollectionFolders(BaseItem item);
-
- List<Folder> GetCollectionFolders(BaseItem item, List<Folder> allUserRootChildren);
-
- LibraryOptions GetLibraryOptions(BaseItem item);
-
- /// <summary>
- /// Gets the people.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>List&lt;PersonInfo&gt;.</returns>
- List<PersonInfo> GetPeople(BaseItem item);
-
- /// <summary>
- /// Gets the people.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;PersonInfo&gt;.</returns>
- List<PersonInfo> GetPeople(InternalPeopleQuery query);
-
- /// <summary>
- /// Gets the people items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;Person&gt;.</returns>
- List<Person> GetPeopleItems(InternalPeopleQuery query);
-
- /// <summary>
- /// Updates the people.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="people">The people.</param>
- void UpdatePeople(BaseItem item, List<PersonInfo> people);
-
- /// <summary>
- /// Gets the item ids.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;Guid&gt;.</returns>
- List<Guid> GetItemIds(InternalItemsQuery query);
-
- /// <summary>
- /// Gets the people names.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;System.String&gt;.</returns>
- List<string> GetPeopleNames(InternalPeopleQuery query);
-
- /// <summary>
- /// Queries the items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
- QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
-
- string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null);
-
- /// <summary>
- /// Substitutes the path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="from">From.</param>
- /// <param name="to">To.</param>
- /// <returns>System.String.</returns>
- string SubstitutePath(string path, string from, string to);
-
- /// <summary>
- /// Converts the image to local.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task.</returns>
- 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>
- List<BaseItem> GetItemList(InternalItemsQuery query);
-
- List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent);
-
- /// <summary>
- /// Gets the items.
- /// </summary>
- List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
-
- /// <summary>
- /// Gets the items result.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
- QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query);
-
- /// <summary>
- /// Ignores the file.
- /// </summary>
- /// <param name="file">The file.</param>
- /// <param name="parent">The parent.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
-
- Guid GetStudioId(string name);
-
- Guid GetGenreId(string name);
-
- Guid GetMusicGenreId(string name);
-
- Guid GetGameGenreId(string name);
-
- void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
- void RemoveVirtualFolder(string name, bool refreshLibrary);
- void AddMediaPath(string virtualFolderName, MediaPathInfo path);
- void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
- void RemoveMediaPath(string virtualFolderName, string path);
-
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
-
- int GetCount(InternalItemsQuery query);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
deleted file mode 100644
index e965e47d6..000000000
--- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface ILibraryMonitor : IDisposable
- {
- /// <summary>
- /// Starts this instance.
- /// </summary>
- void Start();
-
- /// <summary>
- /// Stops this instance.
- /// </summary>
- void Stop();
-
- /// <summary>
- /// Reports the file system change beginning.
- /// </summary>
- /// <param name="path">The path.</param>
- void ReportFileSystemChangeBeginning(string path);
-
- /// <summary>
- /// Reports the file system change complete.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="refreshPath">if set to <c>true</c> [refresh path].</param>
- void ReportFileSystemChangeComplete(string path, bool refreshPath);
-
- /// <summary>
- /// Reports the file system changed.
- /// </summary>
- /// <param name="path">The path.</param>
- void ReportFileSystemChanged(string path);
-
- /// <summary>
- /// Determines whether [is path locked] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is path locked] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsPathLocked(string path);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs b/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs
deleted file mode 100644
index 694422907..000000000
--- a/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// An interface for tasks that run after the media library scan
- /// </summary>
- public interface ILibraryPostScanTask
- {
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task Run(IProgress<double> progress, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
deleted file mode 100644
index 204033e1d..000000000
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using System.IO;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface IMediaSourceManager
- {
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="providers">The providers.</param>
- void AddParts(IEnumerable<IMediaSourceProvider> providers);
-
- /// <summary>
- /// Gets the media streams.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- 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>
- List<MediaStream> GetMediaStreams(string mediaSourceId);
- /// <summary>
- /// Gets the media streams.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- List<MediaStream> GetMediaStreams(MediaStreamQuery query);
-
- /// <summary>
- /// Gets the playack media sources.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
- /// <param name="supportedLiveMediaTypes">The supported live media types.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
- Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the static media sources.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <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>
- List<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null);
-
- /// <summary>
- /// Gets the static media source.
- /// </summary>
- /// <returns>MediaSourceInfo.</returns>
- Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken);
-
- /// <summary>
- /// Opens the media source.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
- Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the live stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
- Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken);
-
- Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Closes the media source.
- /// </summary>
- /// <param name="id">The live stream identifier.</param>
- /// <returns>Task.</returns>
- Task CloseLiveStream(string id);
- }
-
- public interface IDirectStreamProvider
- {
- Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
deleted file mode 100644
index 4ec9adf4e..000000000
--- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using System;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface IMediaSourceProvider
- {
- /// <summary>
- /// Gets the media sources.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
- Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Opens the media source.
- /// </summary>
- Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken);
-
- /// <summary>
- /// Closes the media source.
- /// </summary>
- /// <param name="liveStreamId">The live stream identifier.</param>
- /// <returns>Task.</returns>
- Task CloseMediaSource(string liveStreamId);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
deleted file mode 100644
index e09e58302..000000000
--- a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface IMetadataFileSaver : IMetadataSaver
- {
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- string GetSavePath(IHasMetadata item);
- }
-
- public interface IConfigurableProvider
- {
- bool IsEnabled { get; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs
deleted file mode 100644
index f13cf4587..000000000
--- a/MediaBrowser.Controller/Library/IMetadataSaver.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Threading;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface IMetadataSaver
- /// </summary>
- public interface IMetadataSaver
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Determines whether [is enabled for] [the specified item].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="updateType">Type of the update.</param>
- /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
- bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
-
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void Save(IHasMetadata item, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
deleted file mode 100644
index 535e6df7e..000000000
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Dto;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface IMusicManager
- {
- /// <summary>
- /// Gets the instant mix from song.
- /// </summary>
- List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions);
-
- /// <summary>
- /// Gets the instant mix from artist.
- /// </summary>
- List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions);
-
- /// <summary>
- /// Gets the instant mix from genre.
- /// </summary>
- List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions);
- }
-}
diff --git a/MediaBrowser.Controller/Library/ISearchEngine.cs b/MediaBrowser.Controller/Library/ISearchEngine.cs
deleted file mode 100644
index dcf3be9ef..000000000
--- a/MediaBrowser.Controller/Library/ISearchEngine.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Search;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface ILibrarySearchEngine
- /// </summary>
- public interface ISearchEngine
- {
- /// <summary>
- /// Gets the search hints.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{IEnumerable{SearchHintInfo}}.</returns>
- Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
deleted file mode 100644
index cd4bd2e34..000000000
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Threading;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface IUserDataManager
- /// </summary>
- public interface IUserDataManager
- {
- /// <summary>
- /// Occurs when [user data saved].
- /// </summary>
- event EventHandler<UserDataSaveEventArgs> UserDataSaved;
-
- /// <summary>
- /// Saves the user data.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="item">The item.</param>
- /// <param name="userData">The user data.</param>
- /// <param name="reason">The reason.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
-
- UserItemData GetUserData(IHasUserData user, IHasUserData item);
-
- UserItemData GetUserData(string userId, IHasUserData item);
- UserItemData GetUserData(Guid userId, IHasUserData item);
-
- /// <summary>
- /// Gets the user data dto.
- /// </summary>
- UserItemDataDto GetUserDataDto(IHasUserData item, User user);
-
- UserItemDataDto GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, ItemFields[] fields);
-
- /// <summary>
- /// Get all user data for the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <returns></returns>
- List<UserItemData> GetAllUserData(Guid userId);
-
- /// <summary>
- /// Save the all provided user data for the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <param name="userData"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates playstate for an item and returns true or false indicating if it was played to completion
- /// </summary>
- bool UpdatePlayState(BaseItem item, UserItemData data, long? positionTicks);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
deleted file mode 100644
index 03e1d352e..000000000
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface IUserManager
- /// </summary>
- public interface IUserManager
- {
- /// <summary>
- /// Gets the users.
- /// </summary>
- /// <value>The users.</value>
- IEnumerable<User> Users { get; }
-
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- event EventHandler<GenericEventArgs<User>> UserUpdated;
-
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- event EventHandler<GenericEventArgs<User>> UserDeleted;
-
- event EventHandler<GenericEventArgs<User>> UserCreated;
- event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
- event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
- event EventHandler<GenericEventArgs<User>> UserLockedOut;
-
- /// <summary>
- /// Gets a User by Id
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>User.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- User GetUserById(Guid id);
-
- /// <summary>
- /// Gets the user by identifier.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>User.</returns>
- User GetUserById(string id);
-
- /// <summary>
- /// Gets the name of the user by.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- User GetUserByName(string name);
-
- /// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RefreshUsersMetadata(CancellationToken cancellationToken);
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- Task RenameUser(User user, string newName);
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- void UpdateUser(User user);
-
- /// <summary>
- /// Creates the user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- /// <exception cref="System.ArgumentNullException">name</exception>
- /// <exception cref="System.ArgumentException"></exception>
- Task<User> CreateUser(string name);
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- Task DeleteUser(User user);
-
- /// <summary>
- /// Resets the password.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- void ResetPassword(User user);
-
- /// <summary>
- /// Gets the offline user dto.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserDto.</returns>
- UserDto GetOfflineUserDto(User user);
-
- /// <summary>
- /// Resets the easy password.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- void ResetEasyPassword(User user);
-
- /// <summary>
- /// Changes the password.
- /// </summary>
- void ChangePassword(User user, string newPassword, string newPasswordSha1);
-
- /// <summary>
- /// Changes the easy password.
- /// </summary>
- void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1);
-
- /// <summary>
- /// Gets the user dto.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <returns>UserDto.</returns>
- UserDto GetUserDto(User user, string remoteEndPoint = null);
-
- /// <summary>
- /// Authenticates the user.
- /// </summary>
- Task<User> AuthenticateUser(string username, string password, string passwordSha1, string passwordMd5, string remoteEndPoint, bool isUserSession);
-
- /// <summary>
- /// Starts the forgot password process.
- /// </summary>
- /// <param name="enteredUsername">The entered username.</param>
- /// <param name="isInNetwork">if set to <c>true</c> [is in network].</param>
- /// <returns>ForgotPasswordResult.</returns>
- ForgotPasswordResult StartForgotPasswordProcess(string enteredUsername, bool isInNetwork);
-
- /// <summary>
- /// Redeems the password reset pin.
- /// </summary>
- /// <param name="pin">The pin.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- PinRedeemResult RedeemPasswordResetPin(string pin);
-
- /// <summary>
- /// Gets the user policy.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserPolicy.</returns>
- UserPolicy GetUserPolicy(User user);
-
- /// <summary>
- /// Gets the user configuration.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserConfiguration.</returns>
- UserConfiguration GetUserConfiguration(User user);
-
- /// <summary>
- /// Updates the configuration.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="newConfiguration">The new configuration.</param>
- /// <returns>Task.</returns>
- void UpdateConfiguration(string userId, UserConfiguration newConfiguration);
-
- /// <summary>
- /// Updates the user policy.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="userPolicy">The user policy.</param>
- void UpdateUserPolicy(string userId, UserPolicy userPolicy);
-
- /// <summary>
- /// Makes the valid username.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- string MakeValidUsername(string username);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs
deleted file mode 100644
index baecdd748..000000000
--- a/MediaBrowser.Controller/Library/IUserViewManager.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-
-namespace MediaBrowser.Controller.Library
-{
- public interface IUserViewManager
- {
- Task<Folder[]> GetUserViews(UserViewQuery query, CancellationToken cancellationToken);
-
- UserView GetUserSubViewWithName(string name, string parentId, string type, string sortName, CancellationToken cancellationToken);
-
- UserView GetUserSubView(string category, string type, string localizationKey, string sortName, CancellationToken cancellationToken);
-
- List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IntroInfo.cs b/MediaBrowser.Controller/Library/IntroInfo.cs
deleted file mode 100644
index d0e61d0f0..000000000
--- a/MediaBrowser.Controller/Library/IntroInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Library
-{
- public class IntroInfo
- {
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the item id.
- /// </summary>
- /// <value>The item id.</value>
- public Guid? ItemId { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs
deleted file mode 100644
index e671490d3..000000000
--- a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class ItemChangeEventArgs
- /// </summary>
- public class ItemChangeEventArgs
- {
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItem Item { get; set; }
-
- public BaseItem Parent { get; set; }
-
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public ItemUpdateType UpdateReason { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
deleted file mode 100644
index 56392eee7..000000000
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ /dev/null
@@ -1,281 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// These are arguments relating to the file system that are collected once and then referred to
- /// whenever needed. Primarily for entity resolution.
- /// </summary>
- public class ItemResolveArgs : EventArgs
- {
- /// <summary>
- /// The _app paths
- /// </summary>
- private readonly IServerApplicationPaths _appPaths;
-
- public IDirectoryService DirectoryService { get; private set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ItemResolveArgs" /> class.
- /// </summary>
- /// <param name="appPaths">The app paths.</param>
- /// <param name="directoryService">The directory service.</param>
- public ItemResolveArgs(IServerApplicationPaths appPaths, IDirectoryService directoryService)
- {
- _appPaths = appPaths;
- DirectoryService = directoryService;
- }
-
- /// <summary>
- /// Gets the file system children.
- /// </summary>
- /// <value>The file system children.</value>
- public FileSystemMetadata[] FileSystemChildren { get; set; }
-
- public LibraryOptions LibraryOptions { get; set; }
-
- public LibraryOptions GetLibraryOptions()
- {
- return LibraryOptions ?? (LibraryOptions = (Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent)));
- }
-
- /// <summary>
- /// Gets or sets the parent.
- /// </summary>
- /// <value>The parent.</value>
- public Folder Parent { get; set; }
-
- /// <summary>
- /// Gets or sets the file info.
- /// </summary>
- /// <value>The file info.</value>
- public FileSystemMetadata FileInfo { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is directory.
- /// </summary>
- /// <value><c>true</c> if this instance is directory; otherwise, <c>false</c>.</value>
- public bool IsDirectory
- {
- get
- {
- return FileInfo.IsDirectory;
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is vf.
- /// </summary>
- /// <value><c>true</c> if this instance is vf; otherwise, <c>false</c>.</value>
- public bool IsVf
- {
- // we should be considered a virtual folder if we are a child of one of the children of the system root folder.
- // this is a bit of a trick to determine that... the directory name of a sub-child of the root will start with
- // the root but not be equal to it
- get
- {
- if (!IsDirectory)
- {
- return false;
- }
-
- var parentDir = BaseItem.FileSystem.GetDirectoryName(Path) ?? string.Empty;
-
- return parentDir.Length > _appPaths.RootFolderPath.Length
- && parentDir.StartsWith(_appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase);
-
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is physical root.
- /// </summary>
- /// <value><c>true</c> if this instance is physical root; otherwise, <c>false</c>.</value>
- public bool IsPhysicalRoot
- {
- get
- {
- return IsDirectory && BaseItem.FileSystem.AreEqual(Path, _appPaths.RootFolderPath);
- }
- }
-
- /// <summary>
- /// Gets or sets the additional locations.
- /// </summary>
- /// <value>The additional locations.</value>
- private List<string> AdditionalLocations { get; set; }
-
- public bool HasParent<T>()
- where T : Folder
- {
- var parent = Parent;
-
- if (parent != null)
- {
- var item = parent as T;
-
- // Just in case the user decided to nest episodes.
- // Not officially supported but in some cases we can handle it.
- if (item == null)
- {
- var parents = parent.GetParents();
- foreach (var currentParent in parents)
- {
- if (currentParent is T)
- {
- return true;
- }
- }
- }
-
- return item != null;
-
- }
- return false;
- }
-
- /// <summary>
- /// Adds the additional location.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void AddAdditionalLocation(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException();
- }
-
- if (AdditionalLocations == null)
- {
- AdditionalLocations = new List<string>();
- }
-
- AdditionalLocations.Add(path);
- }
-
- /// <summary>
- /// Gets the physical locations.
- /// </summary>
- /// <value>The physical locations.</value>
- public string[] PhysicalLocations
- {
- get
- {
- var paths = string.IsNullOrWhiteSpace(Path) ? new string[] { } : new[] { Path };
- return AdditionalLocations == null ? paths : paths.Concat(AdditionalLocations).ToArray();
- }
- }
-
- /// <summary>
- /// Gets the name of the file system entry by.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>FileSystemInfo.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public FileSystemMetadata GetFileSystemEntryByName(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException();
- }
-
- return GetFileSystemEntryByPath(System.IO.Path.Combine(Path, name));
- }
-
- /// <summary>
- /// Gets the file system entry by path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>FileSystemInfo.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public FileSystemMetadata GetFileSystemEntryByPath(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException();
- }
-
- foreach (var file in FileSystemChildren)
- {
- if (string.Equals(file.FullName, path, StringComparison.Ordinal))
- {
- return file;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Determines whether [contains file system entry by name] [the specified name].
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns><c>true</c> if [contains file system entry by name] [the specified name]; otherwise, <c>false</c>.</returns>
- public bool ContainsFileSystemEntryByName(string name)
- {
- return GetFileSystemEntryByName(name) != null;
- }
-
- public string GetCollectionType()
- {
- return CollectionType;
- }
-
- public string CollectionType { get; set; }
-
- #region Equality Overrides
-
- /// <summary>
- /// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
- /// </summary>
- /// <param name="obj">The object to compare with the current object.</param>
- /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
- public override bool Equals(object obj)
- {
- return Equals(obj as ItemResolveArgs);
- }
-
- /// <summary>
- /// Returns a hash code for this instance.
- /// </summary>
- /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
- public override int GetHashCode()
- {
- return Path.GetHashCode();
- }
-
- /// <summary>
- /// Equalses the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- protected bool Equals(ItemResolveArgs args)
- {
- if (args != null)
- {
- if (args.Path == null && Path == null) return true;
- return args.Path != null && BaseItem.FileSystem.AreEqual(args.Path, Path);
- }
- return false;
- }
-
- #endregion
- }
-
-}
diff --git a/MediaBrowser.Controller/Library/ItemUpdateType.cs b/MediaBrowser.Controller/Library/ItemUpdateType.cs
deleted file mode 100644
index cf6263356..000000000
--- a/MediaBrowser.Controller/Library/ItemUpdateType.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Library
-{
- [Flags]
- public enum ItemUpdateType
- {
- None = 1,
- MetadataImport = 2,
- ImageUpdate = 4,
- MetadataDownload = 8,
- MetadataEdit = 16
- }
-}
diff --git a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs b/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs
deleted file mode 100644
index ec69bea6e..000000000
--- a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Library
-{
- public static class LibraryManagerExtensions
- {
- public static BaseItem GetItemById(this ILibraryManager manager, string id)
- {
- return manager.GetItemById(new Guid(id));
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs b/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs
deleted file mode 100644
index 6fad786a2..000000000
--- a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Library
-{
- public class MetadataConfigurationStore : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "metadata",
- ConfigurationType = typeof(MetadataConfiguration)
- }
- };
- }
- }
-
- public static class MetadataConfigurationExtensions
- {
- public static MetadataConfiguration GetMetadataConfiguration(this IConfigurationManager config)
- {
- return config.GetConfiguration<MetadataConfiguration>("metadata");
- }
- }
-}
diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs
deleted file mode 100644
index bab334a6d..000000000
--- a/MediaBrowser.Controller/Library/NameExtensions.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Controller.Library
-{
- public static class NameExtensions
- {
- private static string RemoveDiacritics(string name)
- {
- if (name == null)
- {
- return string.Empty;
- }
-
- //return name;
- return name.RemoveDiacritics();
- }
-
- public static IEnumerable<string> DistinctNames(this IEnumerable<string> names)
- {
- return names.DistinctBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
deleted file mode 100644
index 9f98182ba..000000000
--- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Holds information about a playback progress event
- /// </summary>
- public class PlaybackProgressEventArgs : EventArgs
- {
- public List<User> Users { get; set; }
- public long? PlaybackPositionTicks { get; set; }
- public BaseItem Item { get; set; }
- public BaseItemDto MediaInfo { get; set; }
- public string MediaSourceId { get; set; }
- public bool IsPaused { get; set; }
- public bool IsAutomated { get; set; }
-
- public string DeviceId { get; set; }
- public string DeviceName { get; set; }
- public string ClientName { get; set; }
-
- public string PlaySessionId { get; set; }
-
- public PlaybackProgressEventArgs()
- {
- Users = new List<User>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs
deleted file mode 100644
index b0f6799fc..000000000
--- a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Controller.Library
-{
- public class PlaybackStopEventArgs : PlaybackProgressEventArgs
- {
- /// <summary>
- /// Gets or sets a value indicating whether [played to completion].
- /// </summary>
- /// <value><c>true</c> if [played to completion]; otherwise, <c>false</c>.</value>
- public bool PlayedToCompletion { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs
deleted file mode 100644
index d3a754dc9..000000000
--- a/MediaBrowser.Controller/Library/Profiler.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.Diagnostics;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class Profiler
- /// </summary>
- public class Profiler : IDisposable
- {
- /// <summary>
- /// The name
- /// </summary>
- readonly string _name;
- /// <summary>
- /// The stopwatch
- /// </summary>
- readonly Stopwatch _stopwatch;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Profiler" /> class.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="logger">The logger.</param>
- public Profiler(string name, ILogger logger)
- {
- this._name = name;
-
- _logger = logger;
-
- _stopwatch = new Stopwatch();
- _stopwatch.Start();
- }
- #region IDisposable Members
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </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)
- {
- if (dispose)
- {
- _stopwatch.Stop();
- string message;
- if (_stopwatch.ElapsedMilliseconds > 300000)
- {
- message = string.Format("{0} took {1} minutes.",
- _name, ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F"));
- }
- else
- {
- message = string.Format("{0} took {1} seconds.",
- _name, ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000"));
- }
- _logger.Info(message);
- }
- }
-
- #endregion
- }
-}
diff --git a/MediaBrowser.Controller/Library/SearchHintInfo.cs b/MediaBrowser.Controller/Library/SearchHintInfo.cs
deleted file mode 100644
index f832811c2..000000000
--- a/MediaBrowser.Controller/Library/SearchHintInfo.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class SearchHintInfo
- /// </summary>
- public class SearchHintInfo
- {
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItem Item { get; set; }
-
- /// <summary>
- /// Gets or sets the matched term.
- /// </summary>
- /// <value>The matched term.</value>
- public string MatchedTerm { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs
deleted file mode 100644
index 7c82ec293..000000000
--- a/MediaBrowser.Controller/Library/TVUtils.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class TVUtils
- /// </summary>
- public static class TVUtils
- {
- /// <summary>
- /// The TVDB API key
- /// </summary>
- public static readonly string TvdbApiKey = "B89CE93890E9419B";
- /// <summary>
- /// The banner URL
- /// </summary>
- public static readonly string BannerUrl = "https://www.thetvdb.com/banners/";
-
- /// <summary>
- /// Gets the air days.
- /// </summary>
- /// <param name="day">The day.</param>
- /// <returns>List{DayOfWeek}.</returns>
- public static DayOfWeek[] GetAirDays(string day)
- {
- if (!string.IsNullOrWhiteSpace(day))
- {
- if (day.Equals("Daily", StringComparison.OrdinalIgnoreCase))
- {
- return new DayOfWeek[]
- {
- DayOfWeek.Sunday,
- DayOfWeek.Monday,
- DayOfWeek.Tuesday,
- DayOfWeek.Wednesday,
- DayOfWeek.Thursday,
- DayOfWeek.Friday,
- DayOfWeek.Saturday
- };
- }
-
- DayOfWeek value;
-
- if (Enum.TryParse(day, true, out value))
- {
- return new DayOfWeek[]
- {
- value
- };
- }
-
- return new DayOfWeek[]{};
- }
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
deleted file mode 100644
index 654c6b581..000000000
--- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Class UserDataSaveEventArgs
- /// </summary>
- public class UserDataSaveEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public Guid UserId { get; set; }
-
- public List<string> Keys { get; set; }
-
- /// <summary>
- /// Gets or sets the save reason.
- /// </summary>
- /// <value>The save reason.</value>
- public UserDataSaveReason SaveReason { get; set; }
-
- /// <summary>
- /// Gets or sets the user data.
- /// </summary>
- /// <value>The user data.</value>
- public UserItemData UserData { get; set; }
-
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public IHasUserData Item { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs
deleted file mode 100644
index 892a7d5b7..000000000
--- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- /// <summary>
- /// Class ChannelInfo
- /// </summary>
- public class ChannelInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the number.
- /// </summary>
- /// <value>The number.</value>
- public string Number { get; set; }
-
- /// <summary>
- /// Get or sets the Id.
- /// </summary>
- /// <value>The id of the channel.</value>
- public string Id { get; set; }
-
- public string Path { get; set; }
-
- public string TunerChannelId { get; set; }
-
- public string CallSign { get; set; }
-
- /// <summary>
- /// Gets or sets the tuner host identifier.
- /// </summary>
- /// <value>The tuner host identifier.</value>
- public string TunerHostId { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType ChannelType { get; set; }
-
- /// <summary>
- /// Supply the image path if it can be accessed directly from the file system
- /// </summary>
- /// <value>The image path.</value>
- public string ImagePath { get; set; }
-
- /// <summary>
- /// Supply the image url if it can be downloaded
- /// </summary>
- /// <value>The image URL.</value>
- public string ImageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has image.
- /// </summary>
- /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
- public bool? HasImage { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
- public bool? IsFavorite { get; set; }
-
- public bool? IsHD { get; set; }
- public string AudioCodec { get; set; }
- public string VideoCodec { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
deleted file mode 100644
index faf4a34df..000000000
--- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public interface IListingsProvider
- {
- string Name { get; }
- string Type { get; }
- Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
- Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings);
- Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location);
- Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
deleted file mode 100644
index 4934cc1ca..000000000
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ /dev/null
@@ -1,372 +0,0 @@
-using System;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Querying;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- /// <summary>
- /// Manages all live tv services installed on the server
- /// </summary>
- public interface ILiveTvManager
- {
- /// <summary>
- /// Gets the services.
- /// </summary>
- /// <value>The services.</value>
- IReadOnlyList<ILiveTvService> Services { get; }
-
- /// <summary>
- /// Gets the new timer defaults asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{TimerInfo}.</returns>
- Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the new timer defaults.
- /// </summary>
- /// <param name="programId">The program identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SeriesTimerInfoDto}.</returns>
- Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the recording.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task DeleteRecording(string id);
-
- /// <summary>
- /// Deletes the recording.
- /// </summary>
- /// <param name="recording">The recording.</param>
- /// <returns>Task.</returns>
- Task DeleteRecording(BaseItem recording);
-
- /// <summary>
- /// Cancels the timer.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelTimer(string id);
-
- /// <summary>
- /// Cancels the series timer.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelSeriesTimer(string id);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="services">The services.</param>
- /// <param name="tunerHosts">The tuner hosts.</param>
- /// <param name="listingProviders">The listing providers.</param>
- void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
-
- /// <summary>
- /// Gets the timer.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{TimerInfoDto}.</returns>
- Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the series timer.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{TimerInfoDto}.</returns>
- Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recordings.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>QueryResult{RecordingInfoDto}.</returns>
- Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
- QueryResult<BaseItemDto> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the timers.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{TimerInfoDto}}.</returns>
- Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the series timers.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{SeriesTimerInfoDto}}.</returns>
- Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Channel.</returns>
- LiveTvChannel GetInternalChannel(string id);
-
- /// <summary>
- /// Gets the recording.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>LiveTvRecording.</returns>
- Task<BaseItem> GetInternalRecording(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<MediaSourceInfo> GetRecordingStream(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="mediaSourceId">The media source identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{StreamResponseInfo}.</returns>
- Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the program.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task{ProgramInfoDto}.</returns>
- Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null);
-
- /// <summary>
- /// Gets the programs.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IEnumerable{ProgramInfo}.</returns>
- Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the timer.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the timer.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the timer.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateTimer(TimerInfoDto timer, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the series timer.
- /// </summary>
- /// <param name="timer">The timer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording groups.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{QueryResult{RecordingGroupDto}}.</returns>
- Task<QueryResult<BaseItemDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken);
-
- /// <summary>
- /// Closes the live stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CloseLiveStream(string id);
-
- /// <summary>
- /// Gets the guide information.
- /// </summary>
- /// <returns>GuideInfo.</returns>
- GuideInfo GetGuideInfo();
-
- /// <summary>
- /// Gets the recommended programs.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- QueryResult<BaseItemDto> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recommended programs internal.
- /// </summary>
- QueryResult<BaseItem> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the live tv information.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{LiveTvInfo}.</returns>
- Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken);
-
- /// <summary>
- /// Resets the tuner.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ResetTuner(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the live tv folder.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- Folder GetInternalLiveTvFolder(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the live tv folder.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>BaseItemDto.</returns>
- BaseItemDto GetLiveTvFolder(string userId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the enabled users.
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- IEnumerable<User> GetEnabledUsers();
-
- /// <summary>
- /// Gets the internal channels.
- /// </summary>
- QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the internal recordings.
- /// </summary>
- Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording media sources.
- /// </summary>
- Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel media sources.
- /// </summary>
- Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Adds the information to recording dto.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="dto">The dto.</param>
- /// <param name="user">The user.</param>
- void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null);
-
- /// <summary>
- /// Adds the information to program dto.
- /// </summary>
- /// <param name="programs">The programs.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> programs, ItemFields[] fields, User user = null);
-
- /// <summary>
- /// Saves the tuner host.
- /// </summary>
- Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
- /// <summary>
- /// Saves the listing provider.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="validateLogin">if set to <c>true</c> [validate login].</param>
- /// <param name="validateListings">if set to <c>true</c> [validate listings].</param>
- /// <returns>Task.</returns>
- Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings);
-
- void DeleteListingsProvider(string id);
-
- Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber);
-
- TunerChannelMapping GetTunerChannelMapping(ChannelInfo channel, NameValuePair[] mappings, List<ChannelInfo> providerChannels);
-
- /// <summary>
- /// Gets the lineups.
- /// </summary>
- /// <param name="providerType">Type of the provider.</param>
- /// <param name="providerId">The provider identifier.</param>
- /// <param name="country">The country.</param>
- /// <param name="location">The location.</param>
- /// <returns>Task&lt;List&lt;NameIdPair&gt;&gt;.</returns>
- Task<List<NameIdPair>> GetLineups(string providerType, string providerId, string country, string location);
-
- /// <summary>
- /// Gets the registration information.
- /// </summary>
- /// <param name="feature">The feature.</param>
- /// <returns>Task&lt;MBRegistrationRecord&gt;.</returns>
- Task<MBRegistrationRecord> GetRegistrationInfo(string feature);
-
- /// <summary>
- /// Adds the channel information.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="options">The options.</param>
- /// <param name="user">The user.</param>
- void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> items, DtoOptions options, User user);
-
- Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
- Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
-
- List<IListingsProvider> ListingProviders { get; }
-
- List<NameIdPair> GetTunerHostTypes();
- Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
-
- event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
- event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
- event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
- event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
-
- string GetEmbyTvActiveRecordingPath(string id);
- Task<ILiveStream> GetEmbyTvLiveStream(string id);
-
- ActiveRecordingInfo GetActiveRecordingInfo(string path);
-
- void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null);
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
deleted file mode 100644
index ed3b74bf9..000000000
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public interface ILiveTvRecording : IHasMetadata, IHasMediaSources, IHasUserData, IHasStartDate, IHasProgramAttributes
- {
- string ServiceName { get; set; }
- string ExternalId { get; set; }
- string ChannelId { get; }
- string MediaType { get; }
-
- string Container { get; }
-
- string GetClientTypeName();
-
- bool IsParentalAllowed(User user);
-
- Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
-
- PlayAccess GetPlayAccess(User user);
-
- bool CanDelete();
-
- bool CanDelete(User user);
-
- string SeriesTimerId { get; set; }
- string TimerId { get; set; }
- RecordingStatus Status { get; set; }
- DateTime? EndDate { get; set; }
- DateTime DateCreated { get; set; }
- }
-
- public class ActiveRecordingInfo
- {
- public string Id { get; set; }
- public string Path { get; set; }
- public TimerInfo Timer { get; set; }
- public CancellationTokenSource CancellationTokenSource { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
deleted file mode 100644
index cea2d6e21..000000000
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ /dev/null
@@ -1,259 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- /// <summary>
- /// Represents a single live tv back end (next pvr, media portal, etc).
- /// </summary>
- public interface ILiveTvService
- {
- /// <summary>
- /// Occurs when [data source changed].
- /// </summary>
- event EventHandler DataSourceChanged;
-
- /// <summary>
- /// Occurs when [recording status changed].
- /// </summary>
- event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged;
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- string HomePageUrl { get; }
-
- /// <summary>
- /// Gets the status information asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{LiveTvServiceStatusInfo}.</returns>
- Task<LiveTvServiceStatusInfo> GetStatusInfoAsync(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channels async.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ChannelInfo}}.</returns>
- Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken);
-
- /// <summary>
- /// Cancels the timer asynchronous.
- /// </summary>
- /// <param name="timerId">The timer identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CancelTimerAsync(string timerId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Cancels the series timer asynchronous.
- /// </summary>
- /// <param name="timerId">The timer identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the recording asynchronous.
- /// </summary>
- /// <param name="recordingId">The recording identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the series timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the series timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ChannelInfo
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
- /// </summary>
- /// <param name="recordingId">The recording identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ImageResponseInfo}.</returns>
- Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
- /// </summary>
- /// <param name="programId">The program identifier.</param>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ImageResponseInfo}.</returns>
- Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recordings asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RecordingInfo}}.</returns>
- Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recordings asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RecordingInfo}}.</returns>
- Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the new timer defaults asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="program">The program.</param>
- /// <returns>Task{SeriesTimerInfo}.</returns>
- Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null);
-
- /// <summary>
- /// Gets the series timers asynchronous.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{SeriesTimerInfo}}.</returns>
- Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the programs asynchronous.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="startDateUtc">The start date UTC.</param>
- /// <param name="endDateUtc">The end date UTC.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
- Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording stream.
- /// </summary>
- /// <param name="recordingId">The recording identifier.</param>
- /// <param name="streamId">The stream identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<MediaSourceInfo> GetRecordingStream(string recordingId, string streamId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel stream.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="streamId">The stream identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the channel stream media sources.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
- Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the recording stream media sources.
- /// </summary>
- /// <param name="recordingId">The recording identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
- Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken);
-
- /// <summary>
- /// Closes the live stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task CloseLiveStream(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Records the live stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RecordLiveStream(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Resets the tuner.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ResetTuner(string id, CancellationToken cancellationToken);
- }
-
- public interface ISupportsNewTimerIds
- {
- /// <summary>
- /// Creates the timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<string> CreateTimer(TimerInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Creates the series timer asynchronous.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken);
- }
-
- public interface ISupportsDirectStreamProvider
- {
- Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken);
- }
-
- public interface ISupportsUpdatingDefaults
- {
- Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
deleted file mode 100644
index 80c40f8bd..000000000
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public interface ITunerHost
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- /// <summary>
- /// Gets the type.
- /// </summary>
- /// <value>The type.</value>
- string Type { get; }
- /// <summary>
- /// Gets the channels.
- /// </summary>
- /// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
- Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
- /// <summary>
- /// Gets the tuner infos.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;List&lt;LiveTvTunerInfo&gt;&gt;.</returns>
- Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken);
- /// <summary>
- /// Gets the channel stream.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="streamId">The stream identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
- Task<ILiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
- /// <summary>
- /// Gets the channel stream media sources.
- /// </summary>
- /// <param name="channelId">The channel identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
- Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
-
- Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken);
- bool IsSupported
- {
- get;
- }
- }
- public interface IConfigurableTunerHost
- {
- /// <summary>
- /// Validates the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>Task.</returns>
- Task Validate(TunerHostInfo info);
- }
-
- public interface ILiveStream
- {
- Task Open(CancellationToken openCancellationToken);
- void Close();
- int ConsumerCount { get; }
- string OriginalStreamId { get; set; }
- string TunerHostId { get; }
- bool EnableStreamSharing { get; set; }
- MediaSourceInfo OpenedMediaSource { get; set; }
- string UniqueId { get; }
- List<string> SharedStreamIds { get; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
deleted file mode 100644
index bd84541f8..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvAudioRecording : Audio, ILiveTvRecording
- {
- [IgnoreDataMember]
- public string EpisodeTitle { get; set; }
- [IgnoreDataMember]
- public bool IsSeries { get; set; }
- public string SeriesTimerId { get; set; }
- public string TimerId { get; set; }
- [IgnoreDataMember]
- public DateTime StartDate { get; set; }
- public RecordingStatus Status { get; set; }
- [IgnoreDataMember]
- public bool IsSports { get; set; }
- [IgnoreDataMember]
- public bool IsNews { get; set; }
- [IgnoreDataMember]
- public bool IsKids { get; set; }
- [IgnoreDataMember]
- public bool IsRepeat { get; set; }
- [IgnoreDataMember]
- public bool IsMovie { get; set; }
- [IgnoreDataMember]
- public bool IsLive { get; set; }
- [IgnoreDataMember]
- public bool IsPremiere { get; set; }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.LiveTV; }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPositionTicksResume
- {
- get
- {
- return true;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Audio;
- }
- }
-
- [IgnoreDataMember]
- public override LocationType LocationType
- {
- get
- {
- if (!string.IsNullOrEmpty(Path))
- {
- return base.LocationType;
- }
-
- return LocationType.Remote;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return LiveTvProgram.GetDefaultPrimaryImageAspectRatio(this);
- }
-
- public override string GetClientTypeName()
- {
- return "Recording";
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return false;
- }
-
- [IgnoreDataMember]
- public override bool SupportsLocalMetadata
- {
- get
- {
- return false;
- }
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.LiveTvProgram;
- }
-
- protected override string GetInternalMetadataPath(string basePath)
- {
- return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
- }
-
- public override bool CanDelete()
- {
- if (string.Equals(ServiceName, "Emby", StringComparison.OrdinalIgnoreCase))
- {
- return Status == RecordingStatus.Completed;
- }
- return true;
- }
-
- public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
- {
- return user.Policy.EnableLiveTvManagement;
- }
-
- public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
- {
- var list = base.GetMediaSources(enablePathSubstitution);
-
- foreach (var mediaSource in list)
- {
- if (string.IsNullOrWhiteSpace(mediaSource.Path))
- {
- mediaSource.Type = MediaSourceType.Placeholder;
- }
- }
-
- return list;
- }
-
- public override bool IsVisibleStandalone(User user)
- {
- return IsVisible(user);
- }
-
- public override Task Delete(DeleteOptions options)
- {
- return LiveTvManager.DeleteRecording(this);
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
deleted file mode 100644
index 16010b7f5..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ /dev/null
@@ -1,214 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-using System.Globalization;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
- {
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- if (!ConfigurationManager.Configuration.DisableLiveTvChannelUserDataName)
- {
- list.Insert(0, GetClientTypeName() + "-" + Name);
- }
-
- return list;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.LiveTvChannel;
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPositionTicksResume
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.LiveTV; }
- }
-
- [IgnoreDataMember]
- public override bool EnableRememberingTrackSelections
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
- /// Gets or sets the number.
- /// </summary>
- /// <value>The number.</value>
- public string Number { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType ChannelType { get; set; }
-
- [IgnoreDataMember]
- public override LocationType LocationType
- {
- get
- {
- // TODO: This should be removed
- return LocationType.Remote;
- }
- }
-
- protected override string CreateSortName()
- {
- if (!string.IsNullOrEmpty(Number))
- {
- double number = 0;
-
- if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
- {
- return string.Format("{0:00000.0}", number) + "-" + (Name ?? string.Empty);
- }
- }
-
- return (Number ?? string.Empty) + "-" + (Name ?? string.Empty);
- }
-
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
- }
- }
-
- public override string GetClientTypeName()
- {
- return "TvChannel";
- }
-
- public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
- {
- return new List<BaseItem>();
- }
-
- public List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
- {
- var list = new List<MediaSourceInfo>();
-
- var locationType = LocationType;
-
- var info = new MediaSourceInfo
- {
- Id = Id.ToString("N"),
- Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
- MediaStreams = new List<MediaStream>(),
- Name = Name,
- Path = Path,
- RunTimeTicks = RunTimeTicks,
- Type = MediaSourceType.Placeholder,
- IsInfiniteStream = RunTimeTicks == null
- };
-
- list.Add(info);
-
- 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");
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- [IgnoreDataMember]
- public bool IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsKids { get; set; }
-
- [IgnoreDataMember]
- public bool IsPremiere { get; set; }
-
- [IgnoreDataMember]
- public bool IsRepeat { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- [IgnoreDataMember]
- public string EpisodeTitle { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs
deleted file mode 100644
index 682150d35..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.LiveTv
-{
- /// <summary>
- /// Class LiveTvConflictException.
- /// </summary>
- public class LiveTvConflictException : LiveTvException
- {
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvException.cs b/MediaBrowser.Controller/LiveTv/LiveTvException.cs
deleted file mode 100644
index b0a6f75b1..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvException.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- /// <summary>
- /// Class LiveTvException.
- /// </summary>
- public class LiveTvException : Exception
- {
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
deleted file mode 100644
index 9dff18690..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ /dev/null
@@ -1,342 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
- {
- public LiveTvProgram()
- {
- IsVirtualItem = true;
- }
-
- public override List<string> GetUserDataKeys()
- {
- var list = base.GetUserDataKeys();
-
- if (!IsSeries)
- {
- var key = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
-
- key = this.GetProviderId(MetadataProviders.Tmdb);
- if (!string.IsNullOrWhiteSpace(key))
- {
- list.Insert(0, key);
- }
- }
- else if (!string.IsNullOrWhiteSpace(EpisodeTitle))
- {
- var name = GetClientTypeName();
-
- list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty));
- }
-
- return list;
- }
-
- public static double? GetDefaultPrimaryImageAspectRatio(IHasProgramAttributes item)
- {
- var serviceName = item.ServiceName;
-
- if (item.IsMovie)
- {
- if (string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || string.Equals(serviceName, "Next Pvr", StringComparison.OrdinalIgnoreCase))
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
- else
- {
- double value = 16;
- value /= 9;
-
- return value;
- }
- }
- else
- {
- if (string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || string.Equals(serviceName, "Next Pvr", StringComparison.OrdinalIgnoreCase))
- {
- double value = 2;
- value /= 3;
-
- return value;
- }
- else
- {
- double value = 16;
- value /= 9;
-
- return value;
- }
- }
- }
-
- private static string EmbyServiceName = "Emby";
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return GetDefaultPrimaryImageAspectRatio(this);
- }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.LiveTV; }
- }
-
- /// <summary>
- /// The start date of the program, in UTC.
- /// </summary>
- [IgnoreDataMember]
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is repeat.
- /// </summary>
- /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsRepeat { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- [IgnoreDataMember]
- public string EpisodeTitle { get; set; }
-
- [IgnoreDataMember]
- public string ShowId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsKids { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premiere.
- /// </summary>
- /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsPremiere { get; set; }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [IgnoreDataMember]
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- //[IgnoreDataMember]
- //public override string MediaType
- //{
- // get
- // {
- // return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
- // }
- //}
-
- [IgnoreDataMember]
- public bool IsAiring
- {
- get
- {
- var now = DateTime.UtcNow;
-
- return now >= StartDate && now < EndDate;
- }
- }
-
- [IgnoreDataMember]
- public bool HasAired
- {
- get
- {
- var now = DateTime.UtcNow;
-
- return now >= EndDate;
- }
- }
-
- public override string GetClientTypeName()
- {
- return "Program";
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.LiveTvProgram;
- }
-
- protected override string GetInternalMetadataPath(string basePath)
- {
- return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
- }
-
- public override bool CanDelete()
- {
- return false;
- }
-
- public override bool IsInternetMetadataEnabled()
- {
- return false;
- }
-
- public LiveTvProgramLookupInfo GetLookupInfo()
- {
- var info = GetItemLookupInfo<LiveTvProgramLookupInfo>();
- info.IsMovie = IsMovie;
- return info;
- }
-
- [IgnoreDataMember]
- public override bool SupportsPeople
- {
- get
- {
- // Optimization
- if (IsNews || IsSports)
- {
- return false;
- }
-
- return base.SupportsPeople;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsAncestors
- {
- get
- {
- return false;
- }
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return ConfigurationManager.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- private ListingsProviderInfo GetListingsProviderInfo()
- {
- if (string.Equals(ServiceName, "Emby", StringComparison.OrdinalIgnoreCase))
- {
- var config = GetConfiguration();
-
- return config.ListingProviders.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i.MoviePrefix));
- }
-
- return null;
- }
-
- protected override string GetNameForMetadataLookup()
- {
- var name = base.GetNameForMetadataLookup();
-
- var listings = GetListingsProviderInfo();
-
- if (listings != null)
- {
- if (!string.IsNullOrWhiteSpace(listings.MoviePrefix) && name.StartsWith(listings.MoviePrefix, StringComparison.OrdinalIgnoreCase))
- {
- name = name.Substring(listings.MoviePrefix.Length).Trim();
- }
- }
-
- return name;
- }
-
- public override List<ExternalUrl> GetRelatedUrls()
- {
- var list = base.GetRelatedUrls();
-
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
- if (!string.IsNullOrWhiteSpace(imdbId))
- {
- if (IsMovie)
- {
- list.Add(new ExternalUrl
- {
- Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
- });
- }
- }
-
- return list;
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
deleted file mode 100644
index 4da238acf..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvServiceStatusInfo
- {
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvServiceStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the status message.
- /// </summary>
- /// <value>The status message.</value>
- public string StatusMessage { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- public bool HasUpdateAvailable { get; set; }
-
- /// <summary>
- /// Gets or sets the tuners.
- /// </summary>
- /// <value>The tuners.</value>
- public List<LiveTvTunerInfo> Tuners { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is visible.
- /// </summary>
- /// <value><c>true</c> if this instance is visible; otherwise, <c>false</c>.</value>
- public bool IsVisible { get; set; }
-
- public LiveTvServiceStatusInfo()
- {
- Tuners = new List<LiveTvTunerInfo>();
- IsVisible = true;
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
deleted file mode 100644
index 5c001f288..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvTunerInfo
- {
- /// <summary>
- /// Gets or sets the type of the source.
- /// </summary>
- /// <value>The type of the source.</value>
- public string SourceType { get; set; }
-
- /// <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 URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvTunerStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the recording identifier.
- /// </summary>
- /// <value>The recording identifier.</value>
- public string RecordingId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the program.
- /// </summary>
- /// <value>The name of the program.</value>
- public string ProgramName { get; set; }
-
- /// <summary>
- /// Gets or sets the clients.
- /// </summary>
- /// <value>The clients.</value>
- public List<string> Clients { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can reset.
- /// </summary>
- /// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value>
- public bool CanReset { get; set; }
-
- public LiveTvTunerInfo()
- {
- Clients = new List<string>();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
deleted file mode 100644
index 37c1faac6..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvVideoRecording : Video, ILiveTvRecording
- {
- [IgnoreDataMember]
- public string EpisodeTitle { get; set; }
- [IgnoreDataMember]
- public bool IsSeries { get; set; }
- public string SeriesTimerId { get; set; }
- public string TimerId { get; set; }
- [IgnoreDataMember]
- public DateTime StartDate { get; set; }
- public RecordingStatus Status { get; set; }
- [IgnoreDataMember]
- public bool IsSports { get; set; }
- [IgnoreDataMember]
- public bool IsNews { get; set; }
- [IgnoreDataMember]
- public bool IsKids { get; set; }
- [IgnoreDataMember]
- public bool IsRepeat { get; set; }
- [IgnoreDataMember]
- public bool IsMovie { get; set; }
- [IgnoreDataMember]
- public bool IsLive { get; set; }
- [IgnoreDataMember]
- public bool IsPremiere { get; set; }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.LiveTV; }
- }
-
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return Model.Entities.MediaType.Video;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return Status == RecordingStatus.Completed && base.SupportsPlayedStatus;
- }
- }
-
- [IgnoreDataMember]
- public override LocationType LocationType
- {
- get
- {
- if (!string.IsNullOrEmpty(Path))
- {
- return base.LocationType;
- }
-
- return LocationType.Remote;
- }
- }
-
- /// <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>
- [IgnoreDataMember]
- public override bool IsOwnedItem
- {
- get
- {
- return false;
- }
- }
-
- public override string GetClientTypeName()
- {
- return "Recording";
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return false;
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return LiveTvProgram.GetDefaultPrimaryImageAspectRatio(this);
- }
-
- [IgnoreDataMember]
- public override bool SupportsLocalMetadata
- {
- get
- {
- return false;
- }
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.LiveTvProgram;
- }
-
- protected override string GetInternalMetadataPath(string basePath)
- {
- return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
- }
-
- public override bool CanDelete()
- {
- if (string.Equals(ServiceName, "Emby", StringComparison.OrdinalIgnoreCase))
- {
- return Status == RecordingStatus.Completed;
- }
- return true;
- }
-
- public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
- {
- return user.Policy.EnableLiveTvManagement;
- }
-
- public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
- {
- var list = base.GetMediaSources(enablePathSubstitution);
-
- foreach (var mediaSource in list)
- {
- if (string.IsNullOrWhiteSpace(mediaSource.Path))
- {
- mediaSource.Type = MediaSourceType.Placeholder;
- }
- }
-
- return list;
- }
-
- public override bool IsVisibleStandalone(User user)
- {
- return IsVisible(user);
- }
-
- public override Task Delete(DeleteOptions options)
- {
- return LiveTvManager.DeleteRecording(this);
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
deleted file mode 100644
index b0e636d77..000000000
--- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class ProgramInfo
- {
- /// <summary>
- /// Id of the program.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Name of the program
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- public string OfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
- /// <summary>
- /// Gets or sets the short overview.
- /// </summary>
- /// <value>The short overview.</value>
- public string ShortOverview { get; set; }
-
- /// <summary>
- /// The start date of the program, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// The end date of the program, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Genre of the program.
- /// </summary>
- public List<string> Genres { get; set; }
-
- /// <summary>
- /// Gets or sets the original air date.
- /// </summary>
- /// <value>The original air date.</value>
- public DateTime? OriginalAirDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hd.
- /// </summary>
- /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
- public bool? IsHD { get; set; }
-
- public bool? Is3D { get; set; }
-
- /// <summary>
- /// Gets or sets the audio.
- /// </summary>
- /// <value>The audio.</value>
- public ProgramAudio? Audio { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- public float? CommunityRating { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is repeat.
- /// </summary>
- /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
- public bool IsRepeat { get; set; }
-
- public bool IsSubjectToBlackout { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- public string EpisodeTitle { get; set; }
-
- /// <summary>
- /// Supply the image path if it can be accessed directly from the file system
- /// </summary>
- /// <value>The image path.</value>
- public string ImagePath { get; set; }
-
- /// <summary>
- /// Supply the image url if it can be downloaded
- /// </summary>
- /// <value>The image URL.</value>
- public string ImageUrl { get; set; }
-
- public string ThumbImageUrl { get; set; }
-
- public string LogoImageUrl { get; set; }
-
- public string BackdropImageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has image.
- /// </summary>
- /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
- public bool? HasImage { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
- public bool IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- public bool IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- public bool IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- public bool IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- public bool IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- public bool IsKids { get; set; }
-
- public bool IsEducational { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premiere.
- /// </summary>
- /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
- public bool IsPremiere { get; set; }
-
- /// <summary>
- /// Gets or sets the production year.
- /// </summary>
- /// <value>The production year.</value>
- public int? ProductionYear { get; set; }
- /// <summary>
- /// Gets or sets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- public string HomePageUrl { get; set; }
- /// <summary>
- /// Gets or sets the series identifier.
- /// </summary>
- /// <value>The series identifier.</value>
- public string SeriesId { get; set; }
- /// <summary>
- /// Gets or sets the show identifier.
- /// </summary>
- /// <value>The show identifier.</value>
- public string ShowId { get; set; }
- /// <summary>
- /// Gets or sets the season number.
- /// </summary>
- /// <value>The season number.</value>
- public int? SeasonNumber { get; set; }
- /// <summary>
- /// Gets or sets the episode number.
- /// </summary>
- /// <value>The episode number.</value>
- public int? EpisodeNumber { get; set; }
- /// <summary>
- /// Gets or sets the etag.
- /// </summary>
- /// <value>The etag.</value>
- public string Etag { get; set; }
-
- public ProgramInfo()
- {
- Genres = new List<string>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
deleted file mode 100644
index 3ee061360..000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingGroup : Folder
- {
- protected override bool GetBlockUnratedValue(UserPolicy config)
- {
- // Don't block.
- return false;
- }
-
- public override UnratedItem GetBlockUnratedType()
- {
- return UnratedItem.LiveTvProgram;
- }
-
- public override bool SupportsLocalMetadata
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override SourceType SourceType
- {
- get { return SourceType.LiveTV; }
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
deleted file mode 100644
index 3006b9bbe..000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingInfo
- {
- /// <summary>
- /// Id of the recording.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the timer identifier.
- /// </summary>
- /// <value>The timer identifier.</value>
- public string TimerId { get; set; }
-
- /// <summary>
- /// ChannelId of the recording.
- /// </summary>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType ChannelType { get; set; }
-
- /// <summary>
- /// Name of the recording.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
-
- /// <summary>
- /// The start date of the recording, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// The end date of the recording, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public RecordingStatus Status { get; set; }
-
- /// <summary>
- /// Genre of the program.
- /// </summary>
- public List<string> Genres { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is repeat.
- /// </summary>
- /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
- public bool IsRepeat { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- public string EpisodeTitle { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hd.
- /// </summary>
- /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
- public bool? IsHD { get; set; }
-
- /// <summary>
- /// Gets or sets the audio.
- /// </summary>
- /// <value>The audio.</value>
- public ProgramAudio? Audio { get; set; }
-
- /// <summary>
- /// Gets or sets the original air date.
- /// </summary>
- /// <value>The original air date.</value>
- public DateTime? OriginalAirDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
- public bool IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- public bool IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- public bool IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- public bool IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- public bool IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- public bool IsKids { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premiere.
- /// </summary>
- /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
- public bool IsPremiere { get; set; }
-
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- public string OfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- public float? CommunityRating { get; set; }
-
- /// <summary>
- /// Supply the image path if it can be accessed directly from the file system
- /// </summary>
- /// <value>The image path.</value>
- public string ImagePath { get; set; }
-
- /// <summary>
- /// Supply the image url if it can be downloaded
- /// </summary>
- /// <value>The image URL.</value>
- public string ImageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has image.
- /// </summary>
- /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
- public bool? HasImage { get; set; }
- /// <summary>
- /// Gets or sets the show identifier.
- /// </summary>
- /// <value>The show identifier.</value>
- public string ShowId { get; set; }
-
- /// <summary>
- /// Gets or sets the date last updated.
- /// </summary>
- /// <value>The date last updated.</value>
- public DateTime DateLastUpdated { get; set; }
-
- public RecordingInfo()
- {
- Genres = new List<string>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
deleted file mode 100644
index 90ea329fe..000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingStatusChangedEventArgs : EventArgs
- {
- public string RecordingId { get; set; }
-
- public RecordingStatus NewStatus { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
deleted file mode 100644
index 5c73ed833..000000000
--- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class SeriesTimerInfo
- {
- /// <summary>
- /// Id of the recording.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// ChannelId of the recording.
- /// </summary>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- /// <summary>
- /// Name of the recording.
- /// </summary>
- public string Name { get; set; }
-
- public string ServiceName { get; set; }
-
- /// <summary>
- /// Description of the recording.
- /// </summary>
- public string Overview { get; set; }
-
- /// <summary>
- /// The start date of the recording, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// The end date of the recording, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record any time].
- /// </summary>
- /// <value><c>true</c> if [record any time]; otherwise, <c>false</c>.</value>
- public bool RecordAnyTime { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record any channel].
- /// </summary>
- /// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value>
- public bool RecordAnyChannel { get; set; }
-
- public int KeepUpTo { get; set; }
- public KeepUntil KeepUntil { get; set; }
-
- public bool SkipEpisodesInLibrary { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record new only].
- /// </summary>
- /// <value><c>true</c> if [record new only]; otherwise, <c>false</c>.</value>
- public bool RecordNewOnly { get; set; }
-
- /// <summary>
- /// Gets or sets the days.
- /// </summary>
- /// <value>The days.</value>
- public List<DayOfWeek> Days { get; set; }
-
- /// <summary>
- /// Gets or sets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public int Priority { get; set; }
-
- /// <summary>
- /// Gets or sets the pre padding seconds.
- /// </summary>
- /// <value>The pre padding seconds.</value>
- public int PrePaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets the post padding seconds.
- /// </summary>
- /// <value>The post padding seconds.</value>
- public int PostPaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is pre padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value>
- public bool IsPrePaddingRequired { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is post padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
- public bool IsPostPaddingRequired { get; set; }
-
- /// <summary>
- /// Gets or sets the series identifier.
- /// </summary>
- /// <value>The series identifier.</value>
- public string SeriesId { get; set; }
-
- public SeriesTimerInfo()
- {
- Days = new List<DayOfWeek>();
- SkipEpisodesInLibrary = true;
- KeepUntil = KeepUntil.UntilDeleted;
- }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs
deleted file mode 100644
index 642dee3af..000000000
--- a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Controller.LiveTv
-{
- public class TimerEventInfo
- {
- public string Id { get; set; }
- public string ProgramId { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
deleted file mode 100644
index a0002241d..000000000
--- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class TimerInfo
- {
- public TimerInfo()
- {
- Genres = new List<string>();
- KeepUntil = KeepUntil.UntilDeleted;
- }
-
- /// <summary>
- /// Id of the recording.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// ChannelId of the recording.
- /// </summary>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- public string ShowId { get; set; }
-
- /// <summary>
- /// Name of the recording.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Description of the recording.
- /// </summary>
- public string Overview { get; set; }
-
- public string SeriesId { get; set; }
-
- /// <summary>
- /// The start date of the recording, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// The end date of the recording, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public RecordingStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the pre padding seconds.
- /// </summary>
- /// <value>The pre padding seconds.</value>
- public int PrePaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets the post padding seconds.
- /// </summary>
- /// <value>The post padding seconds.</value>
- public int PostPaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is pre padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value>
- public bool IsPrePaddingRequired { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is post padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
- public bool IsPostPaddingRequired { get; set; }
-
- public bool IsManual { get; set; }
-
- /// <summary>
- /// Gets or sets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public int Priority { get; set; }
-
- public int RetryCount { get; set; }
-
- // Program properties
- public int? SeasonNumber { get; set; }
- /// <summary>
- /// Gets or sets the episode number.
- /// </summary>
- /// <value>The episode number.</value>
- public int? EpisodeNumber { get; set; }
- public bool IsMovie { get; set; }
- public bool IsKids { get; set; }
- public bool IsSports { get; set; }
- public bool IsNews { get; set; }
- public bool IsSeries { get; set; }
- public bool IsLive { get; set; }
- public bool IsPremiere { get; set; }
- public int? ProductionYear { get; set; }
- public string EpisodeTitle { get; set; }
- public DateTime? OriginalAirDate { get; set; }
- public bool IsProgramSeries { get; set; }
- public bool IsRepeat { get; set; }
- public string HomePageUrl { get; set; }
- public float? CommunityRating { get; set; }
- public string OfficialRating { get; set; }
- public List<string> Genres { get; set; }
- public string RecordingPath { get; set; }
- public KeepUntil KeepUntil { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs
deleted file mode 100644
index 3b2df0471..000000000
--- a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace MediaBrowser.Controller.LiveTv
-{
- public class TunerChannelMapping
- {
- public string Name { get; set; }
- public string ProviderChannelName { get; set; }
- public string ProviderChannelId { get; set; }
- public string Id { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
deleted file mode 100644
index dafca0598..000000000
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ /dev/null
@@ -1,367 +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>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Controller</RootNamespace>
- <AssemblyName>MediaBrowser.Controller</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- </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>
- <PropertyGroup>
- <RunPostBuildEvent>Always</RunPostBuildEvent>
- </PropertyGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Channels\ChannelItemInfo.cs" />
- <Compile Include="Channels\ChannelItemResult.cs" />
- <Compile Include="Channels\ChannelItemType.cs" />
- <Compile Include="Channels\ChannelMediaInfo.cs" />
- <Compile Include="Channels\ChannelParentalRating.cs" />
- <Compile Include="Channels\ChannelSearchInfo.cs" />
- <Compile Include="Channels\IChannel.cs" />
- <Compile Include="Channels\IChannelManager.cs" />
- <Compile Include="Channels\Channel.cs" />
- <Compile Include="Channels\IHasCacheKey.cs" />
- <Compile Include="Channels\IIndexableChannel.cs" />
- <Compile Include="Channels\InternalAllChannelMediaQuery.cs" />
- <Compile Include="Channels\InternalChannelFeatures.cs" />
- <Compile Include="Channels\InternalChannelItemQuery.cs" />
- <Compile Include="Channels\IRequiresMediaInfoCallback.cs" />
- <Compile Include="Channels\ISearchableChannel.cs" />
- <Compile Include="Chapters\IChapterManager.cs" />
- <Compile Include="Collections\CollectionCreationOptions.cs" />
- <Compile Include="Collections\CollectionEvents.cs" />
- <Compile Include="Collections\ICollectionManager.cs" />
- <Compile Include="Collections\ManualCollectionsFolder.cs" />
- <Compile Include="Connect\ConnectSupporterSummary.cs" />
- <Compile Include="Connect\IConnectManager.cs" />
- <Compile Include="Connect\UserLinkResult.cs" />
- <Compile Include="Devices\CameraImageUploadInfo.cs" />
- <Compile Include="Devices\IDeviceManager.cs" />
- <Compile Include="Devices\IDeviceRepository.cs" />
- <Compile Include="Dlna\ControlRequest.cs" />
- <Compile Include="Dlna\ControlResponse.cs" />
- <Compile Include="Dlna\EventSubscriptionResponse.cs" />
- <Compile Include="Dlna\IConnectionManager.cs" />
- <Compile Include="Dlna\IContentDirectory.cs" />
- <Compile Include="Dlna\IDlnaManager.cs" />
- <Compile Include="Dlna\IEventManager.cs" />
- <Compile Include="Dlna\IMediaReceiverRegistrar.cs" />
- <Compile Include="Dlna\IUpnpService.cs" />
- <Compile Include="Drawing\IImageEncoder.cs" />
- <Compile Include="Drawing\IImageProcessor.cs" />
- <Compile Include="Drawing\ImageCollageOptions.cs" />
- <Compile Include="Drawing\ImageHelper.cs" />
- <Compile Include="Drawing\ImageProcessingOptions.cs" />
- <Compile Include="Drawing\ImageProcessorExtensions.cs" />
- <Compile Include="Drawing\ImageStream.cs" />
- <Compile Include="Dto\DtoOptions.cs" />
- <Compile Include="Dto\IDtoService.cs" />
- <Compile Include="Entities\AudioBook.cs" />
- <Compile Include="Entities\Audio\AudioPodcast.cs" />
- <Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
- <Compile Include="Entities\Audio\IHasMusicGenres.cs" />
- <Compile Include="Entities\Book.cs" />
- <Compile Include="Configuration\IServerConfigurationManager.cs" />
- <Compile Include="Entities\Audio\MusicGenre.cs" />
- <Compile Include="Entities\DayOfWeekHelper.cs" />
- <Compile Include="Entities\Extensions.cs" />
- <Compile Include="Entities\Game.cs" />
- <Compile Include="Entities\GameGenre.cs" />
- <Compile Include="Entities\GameSystem.cs" />
- <Compile Include="Entities\IHasAspectRatio.cs" />
- <Compile Include="Entities\IHasDisplayOrder.cs" />
- <Compile Include="Entities\IHasMediaSources.cs" />
- <Compile Include="Entities\IHasProgramAttributes.cs" />
- <Compile Include="Entities\IHasScreenshots.cs" />
- <Compile Include="Entities\IHasSeries.cs" />
- <Compile Include="Entities\IHasSpecialFeatures.cs" />
- <Compile Include="Entities\IHasStartDate.cs" />
- <Compile Include="Entities\IHasTrailers.cs" />
- <Compile Include="Entities\IHasUserData.cs" />
- <Compile Include="Entities\IHiddenFromDisplay.cs" />
- <Compile Include="Entities\IItemByName.cs" />
- <Compile Include="Entities\IMetadataContainer.cs" />
- <Compile Include="Entities\InternalItemsQuery.cs" />
- <Compile Include="Entities\InternalPeopleQuery.cs" />
- <Compile Include="Entities\ISupportsBoxSetGrouping.cs" />
- <Compile Include="Entities\ISupportsPlaceHolders.cs" />
- <Compile Include="Entities\ItemImageInfo.cs" />
- <Compile Include="Entities\LinkedChild.cs" />
- <Compile Include="Entities\MusicVideo.cs" />
- <Compile Include="Entities\PeopleHelper.cs" />
- <Compile Include="Entities\Photo.cs" />
- <Compile Include="Entities\PhotoAlbum.cs" />
- <Compile Include="Entities\Share.cs" />
- <Compile Include="Entities\SourceType.cs" />
- <Compile Include="Entities\TagExtensions.cs" />
- <Compile Include="Entities\UserView.cs" />
- <Compile Include="Entities\UserViewBuilder.cs" />
- <Compile Include="Extensions\StringExtensions.cs" />
- <Compile Include="IO\StreamHelper.cs" />
- <Compile Include="Library\DeleteOptions.cs" />
- <Compile Include="Library\ILibraryPostScanTask.cs" />
- <Compile Include="Library\IMediaSourceManager.cs" />
- <Compile Include="Library\IMediaSourceProvider.cs" />
- <Compile Include="Library\IMetadataFileSaver.cs" />
- <Compile Include="Library\IMetadataSaver.cs" />
- <Compile Include="Library\IMusicManager.cs" />
- <Compile Include="Library\IntroInfo.cs" />
- <Compile Include="Library\ItemUpdateType.cs" />
- <Compile Include="Library\IUserDataManager.cs" />
- <Compile Include="Library\IUserViewManager.cs" />
- <Compile Include="Library\LibraryManagerExtensions.cs" />
- <Compile Include="Library\MetadataConfigurationStore.cs" />
- <Compile Include="Library\NameExtensions.cs" />
- <Compile Include="Library\PlaybackStopEventArgs.cs" />
- <Compile Include="Library\UserDataSaveEventArgs.cs" />
- <Compile Include="LiveTv\IListingsProvider.cs" />
- <Compile Include="LiveTv\ITunerHost.cs" />
- <Compile Include="LiveTv\RecordingGroup.cs" />
- <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
- <Compile Include="LiveTv\ILiveTvRecording.cs" />
- <Compile Include="LiveTv\LiveTvAudioRecording.cs" />
- <Compile Include="LiveTv\LiveTvChannel.cs" />
- <Compile Include="LiveTv\ChannelInfo.cs" />
- <Compile Include="LiveTv\ILiveTvManager.cs" />
- <Compile Include="LiveTv\ILiveTvService.cs" />
- <Compile Include="LiveTv\LiveTvConflictException.cs" />
- <Compile Include="LiveTv\LiveTvException.cs" />
- <Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
- <Compile Include="LiveTv\LiveTvTunerInfo.cs" />
- <Compile Include="LiveTv\LiveTvProgram.cs" />
- <Compile Include="LiveTv\LiveTvVideoRecording.cs" />
- <Compile Include="LiveTv\ProgramInfo.cs" />
- <Compile Include="LiveTv\RecordingInfo.cs" />
- <Compile Include="LiveTv\SeriesTimerInfo.cs" />
- <Compile Include="LiveTv\TimerEventInfo.cs" />
- <Compile Include="LiveTv\TimerInfo.cs" />
- <Compile Include="LiveTv\TunerChannelMapping.cs" />
- <Compile Include="MediaEncoding\EncodingHelper.cs" />
- <Compile Include="MediaEncoding\EncodingJobInfo.cs" />
- <Compile Include="MediaEncoding\EncodingJobOptions.cs" />
- <Compile Include="MediaEncoding\IEncodingManager.cs" />
- <Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
- <Compile Include="MediaEncoding\IMediaEncoder.cs" />
- <Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
- <Compile Include="MediaEncoding\MediaInfoRequest.cs" />
- <Compile Include="MediaEncoding\MediaStreamSelector.cs" />
- <Compile Include="Net\AuthenticatedAttribute.cs" />
- <Compile Include="Net\AuthorizationInfo.cs" />
- <Compile Include="Net\BasePeriodicWebSocketListener.cs" />
- <Compile Include="Net\IAuthorizationContext.cs" />
- <Compile Include="Net\IAuthService.cs" />
- <Compile Include="Net\IHasResultFactory.cs" />
- <Compile Include="Net\IHttpResultFactory.cs" />
- <Compile Include="Net\IHttpServer.cs" />
- <Compile Include="Net\IServerManager.cs" />
- <Compile Include="Net\ISessionContext.cs" />
- <Compile Include="Net\IWebSocket.cs" />
- <Compile Include="Net\IWebSocketConnection.cs" />
- <Compile Include="Net\IWebSocketListener.cs" />
- <Compile Include="Net\LoggedAttribute.cs" />
- <Compile Include="Net\SecurityException.cs" />
- <Compile Include="Net\StaticResultOptions.cs" />
- <Compile Include="Net\WebSocketConnectEventArgs.cs" />
- <Compile Include="Net\WebSocketMessageInfo.cs" />
- <Compile Include="Notifications\INotificationManager.cs" />
- <Compile Include="Notifications\INotificationService.cs" />
- <Compile Include="Notifications\INotificationsRepository.cs" />
- <Compile Include="Notifications\INotificationTypeFactory.cs" />
- <Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
- <Compile Include="Notifications\UserNotification.cs" />
- <Compile Include="Persistence\MediaStreamQuery.cs" />
- <Compile Include="Playlists\IPlaylistManager.cs" />
- <Compile Include="Playlists\Playlist.cs" />
- <Compile Include="Plugins\ILocalizablePlugin.cs" />
- <Compile Include="Providers\AlbumInfo.cs" />
- <Compile Include="Providers\ArtistInfo.cs" />
- <Compile Include="Providers\BookInfo.cs" />
- <Compile Include="Providers\BoxSetInfo.cs" />
- <Compile Include="Providers\DirectoryService.cs" />
- <Compile Include="Providers\DynamicImageInfo.cs" />
- <Compile Include="Providers\DynamicImageResponse.cs" />
- <Compile Include="Providers\EpisodeInfo.cs" />
- <Compile Include="Providers\ExtraInfo.cs" />
- <Compile Include="Providers\ExtraSource.cs" />
- <Compile Include="Providers\GameInfo.cs" />
- <Compile Include="Providers\GameSystemInfo.cs" />
- <Compile Include="Providers\ICustomMetadataProvider.cs" />
- <Compile Include="Providers\IDirectoryService.cs" />
- <Compile Include="Providers\IDynamicImageProvider.cs" />
- <Compile Include="Providers\IExternalId.cs" />
- <Compile Include="Providers\IExtrasProvider.cs" />
- <Compile Include="Providers\IForcedProvider.cs" />
- <Compile Include="Entities\IHasMetadata.cs" />
- <Compile Include="Providers\IHasItemChangeMonitor.cs" />
- <Compile Include="Providers\IHasLookupInfo.cs" />
- <Compile Include="Providers\IHasOrder.cs" />
- <Compile Include="Providers\IImageProvider.cs" />
- <Compile Include="Providers\ILocalImageFileProvider.cs" />
- <Compile Include="Providers\ILocalMetadataProvider.cs" />
- <Compile Include="Providers\ImageRefreshMode.cs" />
- <Compile Include="Providers\ImageRefreshOptions.cs" />
- <Compile Include="Providers\IPreRefreshProvider.cs" />
- <Compile Include="Providers\IRemoteImageProvider.cs" />
- <Compile Include="Providers\ILocalImageProvider.cs" />
- <Compile Include="Providers\IMetadataProvider.cs" />
- <Compile Include="Providers\IMetadataService.cs" />
- <Compile Include="Providers\IRemoteMetadataProvider.cs" />
- <Compile Include="Providers\IRemoteSearchProvider.cs" />
- <Compile Include="Providers\ItemInfo.cs" />
- <Compile Include="Providers\LiveTvProgramLookupInfo.cs" />
- <Compile Include="Providers\LocalImageInfo.cs" />
- <Compile Include="Providers\MetadataRefreshMode.cs" />
- <Compile Include="Providers\MetadataResult.cs" />
- <Compile Include="Providers\MovieInfo.cs" />
- <Compile Include="Providers\MusicVideoInfo.cs" />
- <Compile Include="Providers\PersonLookupInfo.cs" />
- <Compile Include="Providers\RemoteSearchQuery.cs" />
- <Compile Include="Providers\SeasonInfo.cs" />
- <Compile Include="Providers\SeriesInfo.cs" />
- <Compile Include="Providers\SeriesOrderTypes.cs" />
- <Compile Include="Providers\SongInfo.cs" />
- <Compile Include="Providers\TrailerInfo.cs" />
- <Compile Include="Providers\VideoContentType.cs" />
- <Compile Include="Security\AuthenticationInfo.cs" />
- <Compile Include="Security\AuthenticationInfoQuery.cs" />
- <Compile Include="Security\IAuthenticationRepository.cs" />
- <Compile Include="Security\IEncryptionManager.cs" />
- <Compile Include="Session\AuthenticationRequest.cs" />
- <Compile Include="Sorting\SortHelper.cs" />
- <Compile Include="Subtitles\ISubtitleManager.cs" />
- <Compile Include="Subtitles\ISubtitleProvider.cs" />
- <Compile Include="Providers\ItemLookupInfo.cs" />
- <Compile Include="Providers\MetadataRefreshOptions.cs" />
- <Compile Include="Session\ISessionManager.cs" />
- <Compile Include="Entities\AggregateFolder.cs" />
- <Compile Include="Entities\Audio\Audio.cs" />
- <Compile Include="Entities\Audio\MusicAlbum.cs" />
- <Compile Include="Entities\Audio\MusicArtist.cs" />
- <Compile Include="Entities\BaseItem.cs" />
- <Compile Include="Entities\BasePluginFolder.cs" />
- <Compile Include="Entities\Folder.cs" />
- <Compile Include="Entities\Genre.cs" />
- <Compile Include="Entities\ICollectionFolder.cs" />
- <Compile Include="Entities\IVirtualFolderCreator.cs" />
- <Compile Include="Entities\Movies\BoxSet.cs" />
- <Compile Include="Entities\Movies\Movie.cs" />
- <Compile Include="Entities\Person.cs" />
- <Compile Include="Library\ISearchEngine.cs" />
- <Compile Include="Library\ItemChangeEventArgs.cs" />
- <Compile Include="Library\PlaybackProgressEventArgs.cs" />
- <Compile Include="Entities\Studio.cs" />
- <Compile Include="Entities\Trailer.cs" />
- <Compile Include="Entities\TV\Episode.cs" />
- <Compile Include="Entities\TV\Season.cs" />
- <Compile Include="Entities\TV\Series.cs" />
- <Compile Include="Entities\User.cs" />
- <Compile Include="Entities\UserItemData.cs" />
- <Compile Include="Entities\UserRootFolder.cs" />
- <Compile Include="Entities\Video.cs" />
- <Compile Include="Entities\CollectionFolder.cs" />
- <Compile Include="Entities\Year.cs" />
- <Compile Include="Library\ILibraryMonitor.cs" />
- <Compile Include="IServerApplicationHost.cs" />
- <Compile Include="IServerApplicationPaths.cs" />
- <Compile Include="Library\SearchHintInfo.cs" />
- <Compile Include="Providers\IProviderManager.cs" />
- <Compile Include="MediaEncoding\MediaEncoderHelpers.cs" />
- <Compile Include="Providers\MetadataProviderPriority.cs" />
- <Compile Include="Resolvers\BaseItemResolver.cs" />
- <Compile Include="Resolvers\IItemResolver.cs" />
- <Compile Include="Library\ILibraryManager.cs" />
- <Compile Include="Library\IUserManager.cs" />
- <Compile Include="Library\Profiler.cs" />
- <Compile Include="Persistence\IDisplayPreferencesRepository.cs" />
- <Compile Include="Persistence\IItemRepository.cs" />
- <Compile Include="Persistence\IRepository.cs" />
- <Compile Include="Persistence\IUserDataRepository.cs" />
- <Compile Include="Persistence\IUserRepository.cs" />
- <Compile Include="Library\IIntroProvider.cs" />
- <Compile Include="Plugins\IPluginConfigurationPage.cs" />
- <Compile Include="Plugins\IServerEntryPoint.cs" />
- <Compile Include="Providers\IImageEnhancer.cs" />
- <Compile Include="Resolvers\IResolverIgnoreRule.cs" />
- <Compile Include="Resolvers\ResolverPriority.cs" />
- <Compile Include="Library\TVUtils.cs" />
- <Compile Include="Library\ItemResolveArgs.cs" />
- <Compile Include="IO\FileData.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Session\ISessionController.cs" />
- <Compile Include="Session\ISessionControllerFactory.cs" />
- <Compile Include="Session\SessionEventArgs.cs" />
- <Compile Include="Session\SessionInfo.cs" />
- <Compile Include="Sorting\IBaseItemComparer.cs" />
- <Compile Include="Sorting\IUserBaseItemComparer.cs" />
- <Compile Include="Sorting\SortExtensions.cs" />
- <Compile Include="Subtitles\SubtitleDownloadEventArgs.cs" />
- <Compile Include="Subtitles\SubtitleResponse.cs" />
- <Compile Include="Subtitles\SubtitleSearchRequest.cs" />
- <Compile Include="Sync\IHasDynamicAccess.cs" />
- <Compile Include="Sync\IRemoteSyncProvider.cs" />
- <Compile Include="Sync\IServerSyncProvider.cs" />
- <Compile Include="Sync\ISyncDataProvider.cs" />
- <Compile Include="Sync\ISyncManager.cs" />
- <Compile Include="Sync\ISyncProvider.cs" />
- <Compile Include="Sync\SyncedFileInfo.cs" />
- <Compile Include="Sync\SyncedItemProgress.cs" />
- <Compile Include="TV\ITVSeriesManager.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </ItemGroup>
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
- <PropertyGroup>
- <PreBuildEvent>
- </PreBuildEvent>
- </PropertyGroup>
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
- <!-- 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.Controller/MediaBrowser.Controller.nuget.targets b/MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.Controller/MediaBrowser.Controller.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.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
deleted file mode 100644
index 1fab6defc..000000000
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ /dev/null
@@ -1,2236 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-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
-{
- public class EncodingHelper
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
-
- public EncodingHelper(IMediaEncoder mediaEncoder, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder)
- {
- _mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- }
-
- public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- var defaultEncoder = "libx264";
-
- // 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)
- {
- var hwType = encodingOptions.HardwareAccelerationType;
-
- if (!encodingOptions.EnableHardwareEncoding)
- {
- hwType = null;
- }
-
- if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_qsv", defaultEncoder);
- }
-
- if (string.Equals(hwType, "nvenc", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_nvenc", defaultEncoder);
- }
- if (string.Equals(hwType, "omx", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_omx", defaultEncoder);
- }
- if (string.Equals(hwType, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_v4l2m2m", defaultEncoder);
- }
- if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
- {
- if (IsVaapiSupported(state))
- {
- return GetAvailableEncoder("h264_vaapi", defaultEncoder);
- }
- }
- }
-
- return defaultEncoder;
- }
-
- private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder)
- {
- if (_mediaEncoder.SupportsEncoder(preferredEncoder))
- {
- return preferredEncoder;
- }
- return defaultEncoder;
- }
-
- private bool IsVaapiSupported(EncodingJobInfo state)
- {
- var videoStream = state.VideoStream;
-
- if (videoStream != null)
- {
- // vaapi will throw an error with this input
- // [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99.
- if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// Gets the name of the output video codec
- /// </summary>
- public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- var codec = state.OutputVideoCodec;
-
- if (!string.IsNullOrEmpty(codec))
- {
- if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- return GetH264Encoder(state, encodingOptions);
- }
- if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
- {
- return "libvpx";
- }
- if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
- {
- return "wmv2";
- }
- if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase))
- {
- return "libtheora";
- }
-
- return codec.ToLower();
- }
-
- return "copy";
- }
-
- /// <summary>
- /// Gets the user agent param.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- public string GetUserAgentParam(EncodingJobInfo state)
- {
- string useragent = null;
-
- state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
-
- if (!string.IsNullOrWhiteSpace(useragent))
- {
- return "-user-agent \"" + useragent + "\"";
- }
-
- return string.Empty;
- }
-
- public string GetInputFormat(string container)
- {
- if (string.IsNullOrWhiteSpace(container))
- {
- return null;
- }
-
- container = container.Replace("mkv", "matroska", StringComparison.OrdinalIgnoreCase);
-
- if (string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
- {
- return "mpegts";
- }
-
- // For these need to find out the ffmpeg names
- if (string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "wmv", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "mts", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "vob", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "mpg", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "mpeg", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "rec", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "dvr-ms", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "ogm", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "divx", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(container, "tp", StringComparison.OrdinalIgnoreCase))
- {
- 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))
- {
- return null;
- }
-
- // obviously don't do this for strm files
- if (string.Equals(container, "strm", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- return container;
- }
-
- public string GetDecoderFromCodec(string codec)
- {
- // For these need to find out the ffmpeg names
- if (string.Equals(codec, "mp2", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(codec, "aac_latm", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
- if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- if (_mediaEncoder.SupportsDecoder(codec))
- {
- return codec;
- }
-
- return null;
- }
-
- /// <summary>
- /// Infers the audio codec based on the url
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <returns>System.Nullable{AudioCodecs}.</returns>
- public string InferAudioCodec(string url)
- {
- var ext = Path.GetExtension(url);
-
- 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>
- /// Infers the video codec.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <returns>System.Nullable{VideoCodecs}.</returns>
- public string InferVideoCodec(string url)
- {
- var ext = Path.GetExtension(url);
-
- 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";
- }
-
- public int GetVideoProfileScore(string profile)
- {
- var list = new List<string>
- {
- "Constrained Baseline",
- "Baseline",
- "Extended",
- "Main",
- "High",
- "Progressive High",
- "Constrained High"
- };
-
- // strip spaces because they may be stripped out on the query string
- return Array.FindIndex(list.ToArray(), t => string.Equals(t.Replace(" ", ""), profile.Replace(" ", ""), StringComparison.OrdinalIgnoreCase));
- }
-
- public string GetInputPathArgument(EncodingJobInfo state)
- {
- var protocol = state.InputProtocol;
- var mediaPath = state.MediaPath ?? string.Empty;
-
- var inputPath = new[] { mediaPath };
-
- if (state.IsInputVideo)
- {
- if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
- {
- inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
- }
- }
-
- return _mediaEncoder.GetInputArgument(inputPath, protocol);
- }
-
- /// <summary>
- /// Gets the audio encoder.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- public string GetAudioEncoder(EncodingJobInfo state)
- {
- var codec = state.OutputAudioCodec;
-
- if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
- {
- return "aac -strict experimental";
- }
- if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
- {
- return "libmp3lame";
- }
- if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase))
- {
- return "libvorbis";
- }
- if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase))
- {
- return "wmav2";
- }
- if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
- {
- return "libopus";
- }
-
- return codec.ToLower();
- }
-
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- var request = state.BaseRequest;
-
- var arg = string.Format("-i {0}", GetInputPathArgument(state));
-
- if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
- {
- if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
- {
- if (state.VideoStream != null && state.VideoStream.Width.HasValue)
- {
- // This is hacky but not sure how to get the exact subtitle resolution
- double height = state.VideoStream.Width.Value;
- height /= 16;
- height *= 9;
-
- arg += string.Format(" -canvas_size {0}:{1}", state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture), Convert.ToInt32(height).ToString(CultureInfo.InvariantCulture));
- }
-
- var subtitlePath = state.SubtitleStream.Path;
-
- if (string.Equals(Path.GetExtension(subtitlePath), ".sub", StringComparison.OrdinalIgnoreCase))
- {
- var idxFile = Path.ChangeExtension(subtitlePath, ".idx");
- if (_fileSystem.FileExists(idxFile))
- {
- subtitlePath = idxFile;
- }
- }
-
- arg += " -i \"" + subtitlePath + "\"";
- }
- }
-
- if (state.IsVideoRequest)
- {
- if (GetVideoEncoder(state, encodingOptions).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
- {
- var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- var hwOutputFormat = "vaapi";
-
- if (hasGraphicalSubs)
- {
- hwOutputFormat = "yuv420p";
- }
-
- arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
- }
- }
-
- return arg.Trim();
- }
-
- /// <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>
- public bool IsH264(MediaStream stream)
- {
- var codec = stream.Codec ?? string.Empty;
-
- return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 ||
- codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1;
- }
-
- public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec)
- {
- var bitrate = state.OutputVideoBitrate;
-
- if (bitrate.HasValue)
- {
- if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
- {
- // With vpx when crf is used, b:v becomes a max rate
- // https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
- return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(_usCulture));
- }
-
- if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
- {
- return string.Format(" -b:v {0}", bitrate.Value.ToString(_usCulture));
- }
-
- if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
- {
- // h264
- return string.Format(" -maxrate {0} -bufsize {1}",
- bitrate.Value.ToString(_usCulture),
- (bitrate.Value * 2).ToString(_usCulture));
- }
-
- // h264
- return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
- bitrate.Value.ToString(_usCulture),
- (bitrate.Value * 2).ToString(_usCulture));
- }
-
- return string.Empty;
- }
-
- public string NormalizeTranscodingLevel(string videoCodec, string level)
- {
- double requestLevel;
-
- // Clients may direct play higher than level 41, but there's no reason to transcode higher
- if (double.TryParse(level, NumberStyles.Any, _usCulture, out requestLevel))
- {
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- if (requestLevel > 41)
- {
- return "41";
- }
- }
- }
-
- return level;
- }
-
- /// <summary>
- /// Gets the text subtitle param.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- public string GetTextSubtitleParam(EncodingJobInfo state)
- {
- var seconds = Math.Round(TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds);
-
- // hls always copies timestamps
- var setPtsParam = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive
- ? string.Empty
- : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture));
-
- if (state.SubtitleStream.IsExternal)
- {
- var subtitlePath = state.SubtitleStream.Path;
-
- var charsetParam = string.Empty;
-
- if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
- {
- var charenc = _subtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language, state.MediaSource.Protocol, CancellationToken.None).Result;
-
- if (!string.IsNullOrEmpty(charenc))
- {
- charsetParam = ":charenc=" + charenc;
- }
- }
-
- // TODO: Perhaps also use original_size=1920x800 ??
- return string.Format("subtitles=filename='{0}'{1}{2}",
- _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
- charsetParam,
- setPtsParam);
- }
-
- var mediaPath = state.MediaPath ?? string.Empty;
-
- return string.Format("subtitles='{0}:si={1}'{2}",
- _mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
- state.InternalSubtitleStreamOffset.ToString(_usCulture),
- setPtsParam);
- }
-
- public double? GetFramerateParam(EncodingJobInfo state)
- {
- var request = state.BaseRequest;
-
- if (request.Framerate.HasValue)
- {
- return request.Framerate.Value;
- }
-
- var maxrate = request.MaxFramerate;
-
- if (maxrate.HasValue && state.VideoStream != null)
- {
- var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
-
- if (contentRate.HasValue && contentRate.Value > maxrate.Value)
- {
- return maxrate;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the video bitrate to specify on the command line
- /// </summary>
- public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultH264Preset)
- {
- var param = string.Empty;
-
- var isVc1 = state.VideoStream != null &&
- string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase);
-
- if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
- {
- if (!string.IsNullOrWhiteSpace(encodingOptions.H264Preset))
- {
- param += "-preset " + encodingOptions.H264Preset;
- }
- else
- {
- param += "-preset " + defaultH264Preset;
- }
-
- if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51)
- {
- param += " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- param += " -crf 23";
- }
- }
-
- else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
- {
- param += "-preset fast";
-
- param += " -crf 28";
- }
-
- // h264 (h264_qsv)
- else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- param += "-preset 7 -look_ahead 0";
-
- }
-
- // h264 (h264_nvenc)
- else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
- {
- param += "-preset default";
- }
-
- // webm
- else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase))
- {
- // Values 0-3, 0 being highest quality but slower
- var profileScore = 0;
-
- string crf;
- var qmin = "0";
- var qmax = "50";
-
- crf = "10";
-
- if (isVc1)
- {
- profileScore++;
- }
-
- // Max of 2
- profileScore = Math.Min(profileScore, 2);
-
- // http://www.webmproject.org/docs/encoder-parameters/
- param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
- profileScore.ToString(_usCulture),
- crf,
- qmin,
- qmax);
- }
-
- else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase))
- {
- param += "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2";
- }
-
- // asf/wmv
- else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase))
- {
- param += "-qmin 2";
- }
-
- else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase))
- {
- param += "-mbd 2";
- }
-
- param += GetVideoBitrateParam(state, videoEncoder);
-
- var framerate = GetFramerateParam(state);
- if (framerate.HasValue)
- {
- param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture));
- }
-
- var targetVideoCodec = state.ActualOutputVideoCodec;
-
- var request = state.BaseRequest;
- var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault();
- if (!string.IsNullOrEmpty(profile))
- {
- if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
- {
- // not supported by h264_omx
- param += " -profile:v " + profile;
- }
- }
-
- var level = state.GetRequestedLevel(targetVideoCodec);
-
- if (!string.IsNullOrEmpty(level))
- {
- level = NormalizeTranscodingLevel(state.OutputVideoCodec, level);
-
- // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
- // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
- if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
- {
- switch (level)
- {
- case "30":
- param += " -level 3.0";
- break;
- case "31":
- param += " -level 3.1";
- break;
- case "32":
- param += " -level 3.2";
- break;
- case "40":
- param += " -level 4.0";
- break;
- case "41":
- param += " -level 4.1";
- break;
- case "42":
- param += " -level 4.2";
- break;
- case "50":
- param += " -level 5.0";
- break;
- case "51":
- param += " -level 5.1";
- break;
- case "52":
- param += " -level 5.2";
- break;
- default:
- param += " -level " + level;
- break;
- }
- }
- // nvenc doesn't decode with param -level set ?!
- else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
- {
- //param += "";
- }
- else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase))
- {
- param += " -level " + level;
- }
- }
-
- if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
- {
- param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none";
- }
-
- if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
- {
- param = "-pix_fmt yuv420p " + param;
- }
-
- if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
- {
- param = "-pix_fmt nv21 " + param;
- }
-
- return param;
- }
-
- public bool CanStreamCopyVideo(EncodingJobInfo state, MediaStream videoStream)
- {
- var request = state.BaseRequest;
-
- if (!request.AllowVideoStreamCopy)
- {
- return false;
- }
-
- if (videoStream.IsInterlaced)
- {
- if (state.DeInterlace(videoStream.Codec, false))
- {
- return false;
- }
- }
-
- if (videoStream.IsAnamorphic ?? false)
- {
- if (request.RequireNonAnamorphic)
- {
- return false;
- }
- }
-
- // Can't stream copy if we're burning in subtitles
- if (request.SubtitleStreamIndex.HasValue)
- {
- if (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
- {
- return false;
- }
- }
-
- if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
- {
- if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value && request.RequireAvc)
- {
- return false;
- }
- }
-
- // Source and target codecs must match
- if (string.IsNullOrWhiteSpace(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- var requestedProfiles = state.GetRequestedProfiles(videoStream.Codec);
-
- // If client is requesting a specific video profile, it must match the source
- if (requestedProfiles.Length > 0)
- {
- if (string.IsNullOrWhiteSpace(videoStream.Profile))
- {
- //return false;
- }
-
- var requestedProfile = requestedProfiles[0];
- // strip spaces because they may be stripped out on the query string as well
- if (!string.IsNullOrWhiteSpace(videoStream.Profile) && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", ""), StringComparer.OrdinalIgnoreCase))
- {
- var currentScore = GetVideoProfileScore(videoStream.Profile);
- var requestedScore = GetVideoProfileScore(requestedProfile);
-
- if (currentScore == -1 || currentScore > requestedScore)
- {
- return false;
- }
- }
- }
-
- // Video width must fall within requested value
- if (request.MaxWidth.HasValue)
- {
- if (!videoStream.Width.HasValue || videoStream.Width.Value > request.MaxWidth.Value)
- {
- return false;
- }
- }
-
- // Video height must fall within requested value
- if (request.MaxHeight.HasValue)
- {
- if (!videoStream.Height.HasValue || videoStream.Height.Value > request.MaxHeight.Value)
- {
- return false;
- }
- }
-
- // Video framerate must fall within requested value
- var requestedFramerate = request.MaxFramerate ?? request.Framerate;
- if (requestedFramerate.HasValue)
- {
- var videoFrameRate = videoStream.AverageFrameRate ?? videoStream.RealFrameRate;
-
- if (!videoFrameRate.HasValue || videoFrameRate.Value > requestedFramerate.Value)
- {
- return false;
- }
- }
-
- // Video bitrate must fall within requested value
- if (request.VideoBitRate.HasValue)
- {
- if (!videoStream.BitRate.HasValue || videoStream.BitRate.Value > request.VideoBitRate.Value)
- {
- return false;
- }
- }
-
- if (request.MaxVideoBitDepth.HasValue)
- {
- if (videoStream.BitDepth.HasValue && videoStream.BitDepth.Value > request.MaxVideoBitDepth.Value)
- {
- return false;
- }
- }
-
- if (request.MaxRefFrames.HasValue)
- {
- if (videoStream.RefFrames.HasValue && videoStream.RefFrames.Value > request.MaxRefFrames.Value)
- {
- return false;
- }
- }
-
- // If a specific level was requested, the source must match or be less than
- var level = state.GetRequestedLevel(videoStream.Codec);
- if (!string.IsNullOrEmpty(level))
- {
- double requestLevel;
-
- if (double.TryParse(level, NumberStyles.Any, _usCulture, out requestLevel))
- {
- if (!videoStream.Level.HasValue)
- {
- //return false;
- }
-
- if (videoStream.Level.HasValue && videoStream.Level.Value > requestLevel)
- {
- return false;
- }
- }
- }
-
- return request.EnableAutoStreamCopy;
- }
-
- public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, List<string> supportedAudioCodecs)
- {
- var request = state.BaseRequest;
-
- if (!request.AllowAudioStreamCopy)
- {
- return false;
- }
-
- // Source and target codecs must match
- if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- // Channels must fall within requested value
- var channels = request.AudioChannels ?? request.MaxAudioChannels;
- if (channels.HasValue)
- {
- if (!audioStream.Channels.HasValue || audioStream.Channels.Value <= 0)
- {
- return false;
- }
- if (audioStream.Channels.Value > channels.Value)
- {
- return false;
- }
- }
-
- // Sample rate must fall within requested value
- if (request.AudioSampleRate.HasValue)
- {
- if (!audioStream.SampleRate.HasValue || audioStream.SampleRate.Value <= 0)
- {
- return false;
- }
- if (audioStream.SampleRate.Value > request.AudioSampleRate.Value)
- {
- 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;
- }
- }
-
- return request.EnableAutoStreamCopy;
- }
-
- public int? GetVideoBitrateParamValue(BaseEncodingJobOptions request, MediaStream videoStream, string outputVideoCodec)
- {
- var bitrate = request.VideoBitRate;
-
- if (videoStream != null)
- {
- var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
- request.Height.Value > videoStream.Height.Value;
-
- if (request.Width.HasValue && videoStream.Width.HasValue &&
- request.Width.Value > videoStream.Width.Value)
- {
- isUpscaling = true;
- }
-
- // Don't allow bitrate increases unless upscaling
- if (!isUpscaling)
- {
- if (bitrate.HasValue && videoStream.BitRate.HasValue)
- {
- bitrate = GetMinBitrate(bitrate.Value, videoStream.BitRate.Value);
- }
- }
- }
-
- if (bitrate.HasValue)
- {
- var inputVideoCodec = videoStream == null ? null : videoStream.Codec;
- bitrate = ResolutionNormalizer.ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
-
- // If a max bitrate was requested, don't let the scaled bitrate exceed it
- if (request.VideoBitRate.HasValue)
- {
- bitrate = GetMinBitrate(bitrate.Value, request.VideoBitRate.Value);
- }
- }
-
- return bitrate;
- }
-
- private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
- {
- if (sourceBitrate <= 2000000)
- {
- sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
- }
- else if (sourceBitrate <= 3000000)
- {
- sourceBitrate = Convert.ToInt32(sourceBitrate * 2);
- }
-
- var bitrate = Math.Min(sourceBitrate, requestedBitrate);
-
- return bitrate;
- }
-
- public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
- {
- if (request.AudioBitRate.HasValue)
- {
- // Make sure we don't request a bitrate higher than the source
- var currentBitrate = audioStream == null ? request.AudioBitRate.Value : audioStream.BitRate ?? request.AudioBitRate.Value;
-
- // Don't encode any higher than this
- return Math.Min(384000, request.AudioBitRate.Value);
- //return Math.Min(currentBitrate, request.AudioBitRate.Value);
- }
-
- return null;
- }
-
- public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions, bool isHls)
- {
- var channels = state.OutputAudioChannels;
-
- var filters = new List<string>();
-
- // Boost volume to 200% when downsampling from 6ch to 2ch
- if (channels.HasValue && channels.Value <= 2)
- {
- if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5 && !encodingOptions.DownMixAudioBoost.Equals(1))
- {
- filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture));
- }
- }
-
- var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive;
- if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !isCopyingTimestamps)
- {
- var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds;
-
- filters.Add(string.Format("asetpts=PTS-{0}/TB", Math.Round(seconds).ToString(_usCulture)));
- }
-
- if (filters.Count > 0)
- {
- return "-af \"" + string.Join(",", filters.ToArray()) + "\"";
- }
-
- return string.Empty;
- }
-
- /// <summary>
- /// Gets the number of audio channels to specify on the command line
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="audioStream">The audio stream.</param>
- /// <param name="outputAudioCodec">The output audio codec.</param>
- /// <returns>System.Nullable{System.Int32}.</returns>
- public int? GetNumAudioChannelsParam(BaseEncodingJobOptions request, MediaStream audioStream, string outputAudioCodec)
- {
- var inputChannels = audioStream == null
- ? null
- : audioStream.Channels;
-
- if (inputChannels <= 0)
- {
- inputChannels = null;
- }
-
- int? transcoderChannelLimit = null;
- var codec = outputAudioCodec ?? string.Empty;
-
- if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
- {
- // wmav2 currently only supports two channel output
- transcoderChannelLimit = 2;
- }
-
- else if (codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1)
- {
- // libmp3lame currently only supports two channel output
- transcoderChannelLimit = 2;
- }
- else
- {
- // If we don't have any media info then limit it to 6 to prevent encoding errors due to asking for too many channels
- transcoderChannelLimit = 6;
- }
-
- var isTranscodingAudio = !string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
-
- int? resultChannels = null;
- if (isTranscodingAudio)
- {
- resultChannels = request.TranscodingMaxAudioChannels;
- }
- resultChannels = resultChannels ?? request.MaxAudioChannels ?? request.AudioChannels;
-
- if (inputChannels.HasValue)
- {
- resultChannels = resultChannels.HasValue
- ? Math.Min(resultChannels.Value, inputChannels.Value)
- : inputChannels.Value;
- }
-
- if (isTranscodingAudio && transcoderChannelLimit.HasValue)
- {
- resultChannels = resultChannels.HasValue
- ? Math.Min(resultChannels.Value, transcoderChannelLimit.Value)
- : transcoderChannelLimit.Value;
- }
-
- return resultChannels ?? request.AudioChannels;
- }
-
- /// <summary>
- /// Enforces the resolution limit.
- /// </summary>
- /// <param name="state">The state.</param>
- public void EnforceResolutionLimit(EncodingJobInfo state)
- {
- var videoRequest = state.BaseRequest;
-
- // Switch the incoming params to be ceilings rather than fixed values
- videoRequest.MaxWidth = videoRequest.MaxWidth ?? videoRequest.Width;
- videoRequest.MaxHeight = videoRequest.MaxHeight ?? videoRequest.Height;
-
- videoRequest.Width = null;
- videoRequest.Height = null;
- }
-
- /// <summary>
- /// Gets the fast seek command line parameter.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.String.</returns>
- /// <value>The fast seek command line parameter.</value>
- public string GetFastSeekCommandLineParameter(BaseEncodingJobOptions request)
- {
- var time = request.StartTimeTicks ?? 0;
-
- if (time > 0)
- {
- return string.Format("-ss {0}", _mediaEncoder.GetTimeParameter(time));
- }
-
- return string.Empty;
- }
-
- /// <summary>
- /// Gets the map args.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- public string GetMapArgs(EncodingJobInfo state)
- {
- // If we don't have known media info
- // If input is video, use -sn to drop subtitles
- // Otherwise just return empty
- if (state.VideoStream == null && state.AudioStream == null)
- {
- return state.IsInputVideo ? "-sn" : string.Empty;
- }
-
- // We have media info, but we don't know the stream indexes
- if (state.VideoStream != null && state.VideoStream.Index == -1)
- {
- return "-sn";
- }
-
- // We have media info, but we don't know the stream indexes
- if (state.AudioStream != null && state.AudioStream.Index == -1)
- {
- return state.IsInputVideo ? "-sn" : string.Empty;
- }
-
- var args = string.Empty;
-
- if (state.VideoStream != null)
- {
- args += string.Format("-map 0:{0}", state.VideoStream.Index);
- }
- else
- {
- // No known video stream
- args += "-vn";
- }
-
- if (state.AudioStream != null)
- {
- args += string.Format(" -map 0:{0}", state.AudioStream.Index);
- }
-
- else
- {
- args += " -map -0:a";
- }
-
- var subtitleMethod = state.SubtitleDeliveryMethod;
- if (state.SubtitleStream == null || subtitleMethod == SubtitleDeliveryMethod.Hls)
- {
- args += " -map -0:s";
- }
- else if (subtitleMethod == SubtitleDeliveryMethod.Embed)
- {
- args += string.Format(" -map 0:{0}", state.SubtitleStream.Index);
- }
- else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
- {
- args += " -map 1:0 -sn";
- }
-
- return args;
- }
-
- /// <summary>
- /// Determines which stream will be used for playback
- /// </summary>
- /// <param name="allStream">All stream.</param>
- /// <param name="desiredIndex">Index of the desired.</param>
- /// <param name="type">The type.</param>
- /// <param name="returnFirstIfNoIndex">if set to <c>true</c> [return first if no index].</param>
- /// <returns>MediaStream.</returns>
- public MediaStream GetMediaStream(IEnumerable<MediaStream> allStream, int? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true)
- {
- var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.Index).ToList();
-
- if (desiredIndex.HasValue)
- {
- var stream = streams.FirstOrDefault(s => s.Index == desiredIndex.Value);
-
- if (stream != null)
- {
- return stream;
- }
- }
-
- if (returnFirstIfNoIndex && type == MediaStreamType.Audio)
- {
- return streams.FirstOrDefault(i => i.Channels.HasValue && i.Channels.Value > 0) ??
- streams.FirstOrDefault();
- }
-
- // Just return the first one
- return returnFirstIfNoIndex ? streams.FirstOrDefault() : null;
- }
-
- /// <summary>
- /// Gets the internal graphical subtitle param.
- /// </summary>
- public string GetGraphicalSubtitleParam(EncodingJobInfo state, EncodingOptions options, string outputVideoCodec)
- {
- var outputSizeParam = string.Empty;
-
- var request = state.BaseRequest;
-
- // Add resolution params, if specified
- if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
- {
- outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"');
-
- if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
- {
- outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase));
- }
- else
- {
- outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
- }
- }
-
- if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && outputSizeParam.Length == 0)
- {
- outputSizeParam = ",format=nv12|vaapi,hwupload";
- }
-
- var videoSizeParam = string.Empty;
-
- if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
- {
- videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture));
-
- videoSizeParam += ":force_original_aspect_ratio=decrease";
- }
-
- var mapPrefix = state.SubtitleStream.IsExternal ?
- 1 :
- 0;
-
- var subtitleStreamIndex = state.SubtitleStream.IsExternal
- ? 0
- : state.SubtitleStream.Index;
-
- return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"",
- mapPrefix.ToString(_usCulture),
- subtitleStreamIndex.ToString(_usCulture),
- state.VideoStream.Index.ToString(_usCulture),
- outputSizeParam,
- videoSizeParam);
- }
-
- /// <summary>
- /// If we're going to put a fixed size on the command line, this will calculate it
- /// </summary>
- public string GetOutputSizeParam(EncodingJobInfo state,
- EncodingOptions options,
- string outputVideoCodec,
- bool allowTimeStampCopy = true)
- {
- // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
-
- var request = state.BaseRequest;
-
- var filters = new List<string>();
-
- if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
- {
- filters.Add("format=nv12|vaapi");
- filters.Add("hwupload");
- }
-
- if (state.DeInterlace("h264", true) && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
- {
- // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle
- if (string.Equals(options.DeinterlaceMethod, "bobandweave", StringComparison.OrdinalIgnoreCase) && (state.VideoStream.RealFrameRate ?? 60) <= 30)
- {
- filters.Add("yadif=1:-1:0");
- }
- else
- {
- filters.Add("yadif=0:-1:0");
- }
- }
-
- if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
- {
- // Work around vaapi's reduced scaling features
- var scaler = "scale_vaapi";
-
- // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
- // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
- // output dimensions. Output dimensions are guaranteed to be even.
- decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
- decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
- decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
- decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
- decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
- decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
-
- if (outputWidth > maximumWidth || outputHeight > maximumHeight)
- {
- var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
- outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
- outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
- }
-
- outputWidth = 2 * Math.Truncate(outputWidth / 2);
- outputHeight = 2 * Math.Truncate(outputHeight / 2);
-
- if (outputWidth != inputWidth || outputHeight != inputHeight)
- {
- filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(_usCulture), outputHeight.ToString(_usCulture)));
- }
- }
- else
- {
- var isExynosV4L2 = string.Equals(outputVideoCodec, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase);
-
- // If fixed dimensions were supplied
- if (request.Width.HasValue && request.Height.HasValue)
- {
- var widthParam = request.Width.Value.ToString(_usCulture);
- var heightParam = request.Height.Value.ToString(_usCulture);
-
- if (isExynosV4L2)
- {
- filters.Add(string.Format("scale=trunc({0}/64)*64:trunc({1}/2)*2", widthParam, heightParam));
- }
- else
- {
- filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
- }
- }
-
- // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
- else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(_usCulture);
- var maxHeightParam = request.MaxHeight.Value.ToString(_usCulture);
-
- if (isExynosV4L2)
- {
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/64)*64:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
- }
- else
- {
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
- }
- }
-
- // If a fixed width was requested
- else if (request.Width.HasValue)
- {
- var widthParam = request.Width.Value.ToString(_usCulture);
-
- filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
- }
-
- // If a fixed height was requested
- else if (request.Height.HasValue)
- {
- var heightParam = request.Height.Value.ToString(_usCulture);
-
- if (isExynosV4L2)
- {
- filters.Add(string.Format("scale=trunc(oh*a/64)*64:{0}", heightParam));
- }
- else
- {
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
- }
- }
-
- // If a max width was requested
- else if (request.MaxWidth.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(_usCulture);
-
- if (isExynosV4L2)
- {
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/64)*64:trunc(ow/dar/2)*2", maxWidthParam));
- }
- else
- {
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
- }
- }
-
- // If a max height was requested
- else if (request.MaxHeight.HasValue)
- {
- var maxHeightParam = request.MaxHeight.Value.ToString(_usCulture);
-
- if (isExynosV4L2)
- {
- filters.Add(string.Format("scale=trunc(oh*a/64)*64:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
- }
- else
- {
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
- }
- }
- }
-
- var output = string.Empty;
-
- if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
- {
- var subParam = GetTextSubtitleParam(state);
-
- filters.Add(subParam);
-
- if (allowTimeStampCopy)
- {
- output += " -copyts";
- }
- }
-
- if (filters.Count > 0)
- {
- output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
- }
-
- return output;
- }
-
-
- /// <summary>
- /// Gets the number of threads.
- /// </summary>
- /// <returns>System.Int32.</returns>
- public int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, bool isWebm)
- {
- if (isWebm)
- {
- // Recommended per docs
- return Math.Max(Environment.ProcessorCount - 1, 2);
- }
-
- var threads = state.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount;
-
- // Automatic
- if (threads <= 0 || threads >= Environment.ProcessorCount)
- {
- return 0;
- }
-
- return threads;
- }
-
- public void TryStreamCopy(EncodingJobInfo state)
- {
- if (state.VideoStream != null && CanStreamCopyVideo(state, state.VideoStream))
- {
- state.OutputVideoCodec = "copy";
- }
- else
- {
- var user = state.User;
-
- // If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableVideoPlaybackTranscoding)
- {
- state.OutputVideoCodec = "copy";
- }
- }
-
- if (state.AudioStream != null && CanStreamCopyAudio(state, state.AudioStream, state.SupportedAudioCodecs))
- {
- state.OutputAudioCodec = "copy";
- }
- else
- {
- var user = state.User;
-
- // If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableAudioPlaybackTranscoding)
- {
- state.OutputAudioCodec = "copy";
- }
- }
- }
-
- public static string GetProbeSizeArgument(int numInputFiles)
- {
- return numInputFiles > 1 ? "-probesize 1G" : "";
- }
-
- public static string GetAnalyzeDurationArgument(int numInputFiles)
- {
- return numInputFiles > 1 ? "-analyzeduration 200M" : "";
- }
-
- public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- var inputModifier = string.Empty;
-
- var numInputFiles = state.PlayableStreamFileNames.Length > 0 ? state.PlayableStreamFileNames.Length : 1;
- var probeSizeArgument = GetProbeSizeArgument(numInputFiles);
-
- string analyzeDurationArgument;
- if (state.MediaSource.AnalyzeDurationMs.HasValue)
- {
- analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- analyzeDurationArgument = GetAnalyzeDurationArgument(numInputFiles);
- }
-
- if (!string.IsNullOrWhiteSpace(probeSizeArgument))
- {
- inputModifier += " " + probeSizeArgument;
- }
-
- if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
- {
- inputModifier += " " + analyzeDurationArgument;
- }
-
- inputModifier = inputModifier.Trim();
-
- var userAgentParam = GetUserAgentParam(state);
-
- if (!string.IsNullOrWhiteSpace(userAgentParam))
- {
- inputModifier += " " + userAgentParam;
- }
-
- inputModifier = inputModifier.Trim();
-
- inputModifier += " " + GetFastSeekCommandLineParameter(state.BaseRequest);
- inputModifier = inputModifier.Trim();
-
- if (state.InputProtocol == MediaProtocol.Rtsp)
- {
- inputModifier += " -rtsp_transport tcp -rtsp_transport udp -rtsp_flags prefer_tcp";
- }
-
- if (!string.IsNullOrEmpty(state.InputAudioSync))
- {
- inputModifier += " -async " + state.InputAudioSync;
- }
-
- if (!string.IsNullOrEmpty(state.InputVideoSync))
- {
- inputModifier += " -vsync " + state.InputVideoSync;
- }
-
- if (state.ReadInputAtNativeFramerate && state.InputProtocol != MediaProtocol.Rtsp)
- {
- inputModifier += " -re";
- }
-
- var flags = new List<string>();
- if (state.IgnoreInputDts)
- {
- flags.Add("+igndts");
- }
- if (state.IgnoreInputIndex)
- {
- flags.Add("+ignidx");
- }
- if (state.GenPtsInput)
- {
- flags.Add("+genpts");
- }
- if (state.DiscardCorruptFramesInput)
- {
- flags.Add("+discardcorrupt");
- }
- if (state.EnableFastSeekInput)
- {
- flags.Add("+fastseek");
- }
-
- if (flags.Count > 0)
- {
- inputModifier += " -fflags " + string.Join("", flags.ToArray());
- }
-
- var videoDecoder = GetVideoDecoder(state, encodingOptions);
- if (!string.IsNullOrWhiteSpace(videoDecoder))
- {
- inputModifier += " " + videoDecoder;
- }
-
- if (state.IsVideoRequest)
- {
- var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
-
- // Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
- if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) &&
- state.TranscodingType != TranscodingJobType.Progressive &&
- state.EnableBreakOnNonKeyFrames(outputVideoCodec))
- {
- inputModifier += " -noaccurate_seek";
- }
-
- if (!string.IsNullOrWhiteSpace(state.InputContainer) && state.VideoType == VideoType.VideoFile && string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType))
- {
- var inputFormat = GetInputFormat(state.InputContainer);
- if (!string.IsNullOrWhiteSpace(inputFormat))
- {
- inputModifier += " -f " + inputFormat;
- }
- }
- }
-
- if (state.MediaSource.RequiresLooping)
- {
- inputModifier += " -stream_loop -1";
- }
-
- return inputModifier;
- }
-
-
- public void AttachMediaSourceInfo(EncodingJobInfo state,
- MediaSourceInfo mediaSource,
- string requestedUrl)
- {
- if (state == null)
- {
- throw new ArgumentNullException("state");
- }
- if (mediaSource == null)
- {
- throw new ArgumentNullException("mediaSource");
- }
-
- state.MediaPath = mediaSource.Path;
- state.InputProtocol = mediaSource.Protocol;
- state.InputContainer = mediaSource.Container;
- state.RunTimeTicks = mediaSource.RunTimeTicks;
- state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
-
- state.IsoType = mediaSource.IsoType;
-
- if (mediaSource.VideoType.HasValue)
- {
- state.VideoType = mediaSource.VideoType.Value;
-
- if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, mediaSource.VideoType.Value).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.BluRay)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.BluRay).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.Dvd).Select(Path.GetFileName).ToArray();
- }
- else
- {
- state.PlayableStreamFileNames = new string[] { };
- }
- }
- else
- {
- state.PlayableStreamFileNames = new string[] { };
- }
-
- if (mediaSource.Timestamp.HasValue)
- {
- state.InputTimestamp = mediaSource.Timestamp.Value;
- }
-
- state.InputProtocol = mediaSource.Protocol;
- state.MediaPath = mediaSource.Path;
- state.RunTimeTicks = mediaSource.RunTimeTicks;
- state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
- state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
-
- if (state.ReadInputAtNativeFramerate ||
- mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
- {
- state.InputVideoSync = "-1";
- state.InputAudioSync = "1";
- }
-
- 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";
- }
-
- var mediaStreams = mediaSource.MediaStreams;
-
- if (state.IsVideoRequest)
- {
- var videoRequest = state.BaseRequest;
-
- if (string.IsNullOrEmpty(videoRequest.VideoCodec))
- {
- if (string.IsNullOrWhiteSpace(requestedUrl))
- {
- requestedUrl = "test." + videoRequest.OutputContainer;
- }
-
- videoRequest.VideoCodec = InferVideoCodec(requestedUrl);
- }
-
- state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video);
- state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
- state.SubtitleDeliveryMethod = videoRequest.SubtitleMethod;
- state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
-
- if (state.SubtitleStream != null && !state.SubtitleStream.IsExternal)
- {
- state.InternalSubtitleStreamOffset = mediaStreams.Where(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal).ToList().IndexOf(state.SubtitleStream);
- }
-
- EnforceResolutionLimit(state);
-
- NormalizeSubtitleEmbed(state);
- }
- else
- {
- state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
- }
-
- state.MediaSource = mediaSource;
- }
-
- private void NormalizeSubtitleEmbed(EncodingJobInfo state)
- {
- if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
- {
- return;
- }
-
- // This is tricky to remux in, after converting to dvdsub it's not positioned correctly
- // Therefore, let's just burn it in
- if (string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
- {
- state.SubtitleDeliveryMethod = SubtitleDeliveryMethod.Encode;
- }
- }
-
- /// <summary>
- /// Gets the name of the output video codec
- /// </summary>
- protected string GetVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- 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 (videoType != VideoType.VideoFile)
- {
- return null;
- }
-
- if (videoStream != null &&
- !string.IsNullOrWhiteSpace(videoStream.Codec) &&
- !string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType))
- {
- if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
- {
- switch (videoStream.Codec.ToLower())
- {
- case "avc":
- case "h264":
- if (_mediaEncoder.SupportsDecoder("h264_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
- {
- // qsv decoder does not support 10-bit input
- if ((videoStream.BitDepth ?? 8) > 8)
- {
- return null;
- }
- return "-c:v h264_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") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v mpeg2_qsv ";
- }
- break;
- case "vc1":
- if (_mediaEncoder.SupportsDecoder("vc1_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v vc1_qsv ";
- }
- break;
- }
- }
-
- else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
- {
- switch (videoStream.Codec.ToLower())
- {
- case "avc":
- case "h264":
- 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") && 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;
- }
- }
-
- else if (string.Equals(encodingOptions.HardwareAccelerationType, "omx", StringComparison.OrdinalIgnoreCase))
- {
- switch (videoStream.Codec.ToLower())
- {
- case "avc":
- case "h264":
- if (_mediaEncoder.SupportsDecoder("h264_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v h264_mmal";
- }
- break;
- case "mpeg2video":
- if (_mediaEncoder.SupportsDecoder("mpeg2_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v mpeg2_mmal";
- }
- break;
- }
- }
- }
-
- // leave blank so ffmpeg will decide
- return null;
- }
-
- public string GetSubtitleEmbedArguments(EncodingJobInfo state)
- {
- if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
- {
- return string.Empty;
- }
-
- var format = state.SupportedSubtitleCodecs.FirstOrDefault();
- string codec;
-
- if (string.IsNullOrWhiteSpace(format) || string.Equals(format, state.SubtitleStream.Codec, StringComparison.OrdinalIgnoreCase))
- {
- codec = "copy";
- }
- else
- {
- codec = format;
- }
-
- var args = " -codec:s:0 " + codec;
-
- args += " -disposition:s:0 default";
-
- return args;
- }
-
- public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultH264Preset)
- {
- // Get the output codec name
- var videoCodec = GetVideoEncoder(state, encodingOptions);
-
- var format = string.Empty;
- var keyFrame = string.Empty;
-
- if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
- state.BaseRequest.Context == EncodingContext.Streaming)
- {
- // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
- format = " -f mp4 -movflags frag_keyframe+empty_moov";
- }
-
- var threads = GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
-
- var inputModifier = GetInputModifier(state, encodingOptions);
-
- return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
- inputModifier,
- GetInputArgument(state, encodingOptions),
- keyFrame,
- GetMapArgs(state),
- GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultH264Preset),
- threads,
- GetProgressiveVideoAudioArguments(state, encodingOptions),
- GetSubtitleEmbedArguments(state),
- format,
- outputPath
- ).Trim();
- }
-
- public string GetOutputFFlags(EncodingJobInfo state)
- {
- var flags = new List<string>();
- if (state.GenPtsOutput)
- {
- flags.Add("+genpts");
- }
-
- if (flags.Count > 0)
- {
- return " -fflags " + string.Join("", flags.ToArray());
- }
-
- return string.Empty;
- }
-
- public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultH264Preset)
- {
- var args = "-codec:v:0 " + videoCodec;
-
- if (state.EnableMpegtsM2TsMode)
- {
- args += " -mpegts_m2ts_mode 1";
- }
-
- if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- if (state.VideoStream != null && IsH264(state.VideoStream) &&
- string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
- {
- args += " -bsf:v h264_mp4toannexb";
- }
-
- if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
- {
- args += " -copyts -avoid_negative_ts disabled -start_at_zero";
- }
-
- if (!state.RunTimeTicks.HasValue)
- {
- args += " -flags -global_header -fflags +genpts";
- }
- }
- else
- {
- var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
- 5.ToString(_usCulture));
-
- args += keyFrameArg;
-
- var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
-
- var hasCopyTs = false;
- // Add resolution params, if specified
- if (!hasGraphicalSubs)
- {
- var outputSizeParam = GetOutputSizeParam(state, encodingOptions, videoCodec);
- args += outputSizeParam;
- hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
- }
-
- if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
- {
- if (!hasCopyTs)
- {
- args += " -copyts";
- }
- args += " -avoid_negative_ts disabled -start_at_zero";
- }
-
- // This is for internal graphical subs
- if (hasGraphicalSubs)
- {
- args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
- }
-
- var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
-
- if (!string.IsNullOrEmpty(qualityParam))
- {
- args += " " + qualityParam.Trim();
- }
-
- if (!state.RunTimeTicks.HasValue)
- {
- args += " -flags -global_header";
- }
- }
-
- if (!string.IsNullOrEmpty(state.OutputVideoSync))
- {
- args += " -vsync " + state.OutputVideoSync;
- }
-
- args += GetOutputFFlags(state);
-
- return args;
- }
-
- public string GetProgressiveVideoAudioArguments(EncodingJobInfo state, EncodingOptions encodingOptions)
- {
- // If the video doesn't have an audio stream, return a default.
- if (state.AudioStream == null && state.VideoStream != null)
- {
- return string.Empty;
- }
-
- // Get the output codec name
- var codec = GetAudioEncoder(state);
-
- var args = "-codec:a:0 " + codec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return args;
- }
-
- // Add the number of audio channels
- 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 += " " + GetAudioFilterParam(state, encodingOptions, false);
-
- return args;
- }
-
- public string GetProgressiveAudioFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath)
- {
- var audioTranscodeParams = new List<string>();
-
- var bitrate = state.OutputAudioBitrate;
-
- if (bitrate.HasValue)
- {
- audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(_usCulture));
- }
-
- if (state.OutputAudioChannels.HasValue)
- {
- audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(_usCulture));
- }
-
- // opus will fail on 44100
- if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
- {
- if (state.OutputAudioSampleRate.HasValue)
- {
- audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture));
- }
- }
-
- var albumCoverInput = string.Empty;
- var mapArgs = string.Empty;
- var metadata = string.Empty;
- var vn = string.Empty;
-
- var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath);
- hasArt = false;
-
- if (hasArt)
- {
- albumCoverInput = " -i \"" + state.AlbumCoverPath + "\"";
- mapArgs = " -map 0:a -map 1:v -c:1:v copy";
- metadata = " -metadata:s:v title=\"Album cover\" -metadata:s:v comment=\"Cover(Front)\"";
- }
- else
- {
- vn = " -vn";
- }
-
- var threads = GetNumberOfThreads(state, encodingOptions, false);
-
- var inputModifier = GetInputModifier(state, encodingOptions);
-
- return string.Format("{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"",
- inputModifier,
- GetInputArgument(state, encodingOptions),
- threads,
- vn,
- string.Join(" ", audioTranscodeParams.ToArray()),
- outputPath,
- metadata,
- albumCoverInput,
- mapArgs).Trim();
- }
-
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
deleted file mode 100644
index ad131064c..000000000
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ /dev/null
@@ -1,669 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using MediaBrowser.Controller.Entities;
-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.Drawing;
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- // For now, a common base class until the API and MediaEncoding classes are unified
- public abstract class EncodingJobInfo
- {
- private readonly ILogger _logger;
-
- public MediaStream VideoStream { get; set; }
- public VideoType VideoType { get; set; }
- public Dictionary<string, string> RemoteHttpHeaders { get; set; }
- public string OutputVideoCodec { get; set; }
- public MediaProtocol InputProtocol { get; set; }
- public string MediaPath { get; set; }
- public bool IsInputVideo { get; set; }
- public IIsoMount IsoMount { get; set; }
- public string[] PlayableStreamFileNames { get; set; }
- public string OutputAudioCodec { get; set; }
- public int? OutputVideoBitrate { get; set; }
- public MediaStream SubtitleStream { get; set; }
- public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
- public List<string> SupportedSubtitleCodecs { get; set; }
-
- public int InternalSubtitleStreamOffset { get; set; }
- public MediaSourceInfo MediaSource { get; set; }
- public User User { get; set; }
-
- public long? RunTimeTicks { get; set; }
-
- public bool ReadInputAtNativeFramerate { get; set; }
-
- private TranscodeReason[] _transcodeReasons = null;
- public TranscodeReason[] TranscodeReasons
- {
- get
- {
- if (_transcodeReasons == null)
- {
- _transcodeReasons = (BaseRequest.TranscodeReasons ?? string.Empty)
- .Split(',')
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
- .ToArray();
- }
-
- return _transcodeReasons;
- }
- }
-
- public bool IgnoreInputDts
- {
- get
- {
- return MediaSource.IgnoreDts;
- }
- }
-
- public bool IgnoreInputIndex
- {
- get
- {
- return MediaSource.IgnoreIndex;
- }
- }
-
- public bool GenPtsInput
- {
- get
- {
- return MediaSource.GenPtsInput;
- }
- }
-
- public bool DiscardCorruptFramesInput
- {
- get
- {
- return false;
- }
- }
-
- public bool EnableFastSeekInput
- {
- get
- {
- return false;
- }
- }
-
- public bool GenPtsOutput
- {
- get
- {
- return false;
- }
- }
-
- public string OutputContainer { get; set; }
-
- public string OutputVideoSync
- {
- get
- {
- // For live tv + in progress recordings
- if (string.Equals(InputContainer, "mpegts", StringComparison.OrdinalIgnoreCase) || string.Equals(InputContainer, "ts", StringComparison.OrdinalIgnoreCase))
- {
- if (!MediaSource.RunTimeTicks.HasValue)
- {
- return "cfr";
- }
- }
-
- return "-1";
- }
- }
-
- public string AlbumCoverPath { get; set; }
-
- public string InputAudioSync { get; set; }
- public string InputVideoSync { get; set; }
- public TransportStreamTimestamp InputTimestamp { get; set; }
-
- public MediaStream AudioStream { get; set; }
- public List<string> SupportedAudioCodecs { get; set; }
- public List<string> SupportedVideoCodecs { get; set; }
- public string InputContainer { get; set; }
- public IsoType? IsoType { get; set; }
-
- public bool EnableMpegtsM2TsMode { get; set; }
-
- public BaseEncodingJobOptions BaseRequest { get; set; }
-
- public long? StartTimeTicks
- {
- get { return BaseRequest.StartTimeTicks; }
- }
-
- public bool CopyTimestamps
- {
- get { return BaseRequest.CopyTimestamps; }
- }
-
- public int? OutputAudioBitrate;
- public int? OutputAudioChannels;
-
- public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
- {
- var videoStream = VideoStream;
- var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
-
- if (!isInputInterlaced)
- {
- return false;
- }
-
- // Support general param
- if (BaseRequest.DeInterlace)
- {
- return true;
- }
-
- if (!string.IsNullOrWhiteSpace(videoCodec))
- {
- if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- if (forceDeinterlaceIfSourceIsInterlaced)
- {
- if (isInputInterlaced)
- {
- return true;
- }
- }
-
- return false;
- }
-
- public string[] GetRequestedProfiles(string codec)
- {
- if (!string.IsNullOrWhiteSpace(BaseRequest.Profile))
- {
- return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- if (!string.IsNullOrWhiteSpace(codec))
- {
- var profile = BaseRequest.GetOption(codec, "profile");
-
- if (!string.IsNullOrWhiteSpace(profile))
- {
- return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
- }
-
- return new string[] { };
- }
-
- public string GetRequestedLevel(string codec)
- {
- if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
- {
- return BaseRequest.Level;
- }
-
- if (!string.IsNullOrWhiteSpace(codec))
- {
- return BaseRequest.GetOption(codec, "level");
- }
-
- return null;
- }
-
- public int? GetRequestedMaxRefFrames(string codec)
- {
- if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
- {
- return BaseRequest.MaxRefFrames;
- }
-
- if (!string.IsNullOrWhiteSpace(codec))
- {
- var value = BaseRequest.GetOption(codec, "maxrefframes");
- int result;
- if (!string.IsNullOrWhiteSpace(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
- {
- return result;
- }
- }
-
- return null;
- }
-
- public bool IsVideoRequest { get; set; }
- public TranscodingJobType TranscodingType { get; set; }
-
- public EncodingJobInfo(ILogger logger, TranscodingJobType jobType)
- {
- _logger = logger;
- TranscodingType = jobType;
- RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- PlayableStreamFileNames = new string[] { };
- SupportedAudioCodecs = new List<string>();
- SupportedVideoCodecs = new List<string>();
- SupportedSubtitleCodecs = new List<string>();
- }
-
- public bool IsSegmentedLiveStream
- {
- get
- {
- return TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
- }
- }
-
- public bool EnableBreakOnNonKeyFrames(string videoCodec)
- {
- if (TranscodingType != TranscodingJobType.Progressive)
- {
- if (IsSegmentedLiveStream)
- {
- return false;
- }
-
- return BaseRequest.BreakOnNonKeyFrames && string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase);
- }
-
- return false;
- }
-
- public int? TotalOutputBitrate
- {
- get
- {
- return (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
- }
- }
-
- public int? OutputWidth
- {
- get
- {
- if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
- {
- var size = new ImageSize
- {
- Width = VideoStream.Width.Value,
- Height = VideoStream.Height.Value
- };
-
- var newSize = DrawingUtils.Resize(size,
- BaseRequest.Width,
- BaseRequest.Height,
- BaseRequest.MaxWidth,
- BaseRequest.MaxHeight);
-
- return Convert.ToInt32(newSize.Width);
- }
-
- if (!IsVideoRequest)
- {
- return null;
- }
-
- return BaseRequest.MaxWidth ?? BaseRequest.Width;
- }
- }
-
- public int? OutputHeight
- {
- get
- {
- if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
- {
- var size = new ImageSize
- {
- Width = VideoStream.Width.Value,
- Height = VideoStream.Height.Value
- };
-
- var newSize = DrawingUtils.Resize(size,
- BaseRequest.Width,
- BaseRequest.Height,
- BaseRequest.MaxWidth,
- BaseRequest.MaxHeight);
-
- return Convert.ToInt32(newSize.Height);
- }
-
- if (!IsVideoRequest)
- {
- return null;
- }
-
- return BaseRequest.MaxHeight ?? BaseRequest.Height;
- }
- }
-
- public int? OutputAudioSampleRate
- {
- get
- {
- if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- if (AudioStream != null)
- {
- return AudioStream.SampleRate;
- }
- }
-
- else if (BaseRequest.AudioSampleRate.HasValue)
- {
- // Don't exceed what the encoder supports
- // Seeing issues of attempting to encode to 88200
- return Math.Min(44100, BaseRequest.AudioSampleRate.Value);
- }
-
- return null;
- }
- }
-
- public int? OutputAudioBitDepth
- {
- get
- {
- if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- if (AudioStream != null)
- {
- return AudioStream.BitDepth;
- }
- }
-
- //else if (BaseRequest.AudioSampleRate.HasValue)
- //{
- // // Don't exceed what the encoder supports
- // // Seeing issues of attempting to encode to 88200
- // return Math.Min(44100, BaseRequest.AudioSampleRate.Value);
- //}
-
- return null;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public double? TargetVideoLevel
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? null : VideoStream.Level;
- }
-
- var level = GetRequestedLevel(ActualOutputVideoCodec);
- double result;
- if (!string.IsNullOrWhiteSpace(level) && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
- {
- return result;
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetVideoBitDepth
- {
- get
- {
- var stream = VideoStream;
- return stream == null || !BaseRequest.Static ? null : stream.BitDepth;
- }
- }
-
- /// <summary>
- /// Gets the target reference frames.
- /// </summary>
- /// <value>The target reference frames.</value>
- public int? TargetRefFrames
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? null : VideoStream.RefFrames;
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public float? TargetFramerate
- {
- get
- {
- var stream = VideoStream;
- var requestedFramerate = BaseRequest.MaxFramerate ?? BaseRequest.Framerate;
-
- return requestedFramerate.HasValue && !BaseRequest.Static
- ? requestedFramerate
- : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
- }
- }
-
- public TransportStreamTimestamp TargetTimestamp
- {
- get
- {
- var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
- TransportStreamTimestamp.Valid :
- TransportStreamTimestamp.None;
-
- return !BaseRequest.Static
- ? defaultValue
- : InputTimestamp;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetPacketLength
- {
- get
- {
- var stream = VideoStream;
- return !BaseRequest.Static
- ? null
- : stream == null ? null : stream.PacketLength;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public string TargetVideoProfile
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? null : VideoStream.Profile;
- }
-
- var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
- if (!string.IsNullOrWhiteSpace(requestedProfile))
- {
- return requestedProfile;
- }
-
- return null;
- }
- }
-
- public string TargetVideoCodecTag
- {
- get
- {
- var stream = VideoStream;
- return !BaseRequest.Static
- ? null
- : stream == null ? null : stream.CodecTag;
- }
- }
-
- public bool? IsTargetAnamorphic
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? null : VideoStream.IsAnamorphic;
- }
-
- return false;
- }
- }
-
- 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 bool? IsTargetInterlaced
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
- }
-
- if (DeInterlace(ActualOutputVideoCodec, true))
- {
- return false;
- }
-
- return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
- }
- }
-
- public bool? IsTargetAVC
- {
- get
- {
- if (BaseRequest.Static)
- {
- return VideoStream == null ? null : VideoStream.IsAVC;
- }
-
- return false;
- }
- }
-
- public int? TargetVideoStreamCount
- {
- get
- {
- if (BaseRequest.Static)
- {
- return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
- }
- return GetMediaStreamCount(MediaStreamType.Video, 1);
- }
- }
-
- public int? TargetAudioStreamCount
- {
- get
- {
- if (BaseRequest.Static)
- {
- return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
- }
- return GetMediaStreamCount(MediaStreamType.Audio, 1);
- }
- }
-
- private int? GetMediaStreamCount(MediaStreamType type, int limit)
- {
- var count = MediaSource.GetStreamCount(type);
-
- if (count.HasValue)
- {
- count = Math.Min(count.Value, limit);
- }
-
- return count;
- }
-
- protected void DisposeIsoMount()
- {
- if (IsoMount != null)
- {
- try
- {
- IsoMount.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing iso mount", ex);
- }
-
- IsoMount = null;
- }
- }
-
- public abstract void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate);
- }
-
- /// <summary>
- /// Enum TranscodingJobType
- /// </summary>
- public enum TranscodingJobType
- {
- /// <summary>
- /// The progressive
- /// </summary>
- Progressive,
- /// <summary>
- /// The HLS
- /// </summary>
- Hls,
- /// <summary>
- /// The dash
- /// </summary>
- Dash
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
deleted file mode 100644
index bac2a6e65..000000000
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ /dev/null
@@ -1,261 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class EncodingJobOptions : BaseEncodingJobOptions
- {
- public string OutputDirectory { get; set; }
-
- public string DeviceId { get; set; }
- public string ItemId { get; set; }
- public string MediaSourceId { get; set; }
- public string AudioCodec { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool ReadInputAtNativeFramerate { get; set; }
-
- /// <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;
- }
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
- {
- OutputContainer = info.Container;
- StartTimeTicks = info.StartPositionTicks;
- MaxWidth = info.MaxWidth;
- MaxHeight = info.MaxHeight;
- MaxFramerate = info.MaxFramerate;
- ItemId = info.ItemId;
- MediaSourceId = info.MediaSourceId;
- AudioCodec = info.TargetAudioCodec.FirstOrDefault();
- MaxAudioChannels = info.MaxAudioChannels;
- AudioBitRate = info.AudioBitrate;
- AudioSampleRate = info.TargetAudioSampleRate;
- DeviceProfile = deviceProfile;
- VideoCodec = info.TargetVideoCodec.FirstOrDefault();
- VideoBitRate = info.VideoBitrate;
- AudioStreamIndex = info.AudioStreamIndex;
- MaxVideoBitDepth = info.MaxVideoBitDepth;
- SubtitleMethod = info.SubtitleDeliveryMethod;
- Context = info.Context;
- TranscodingMaxAudioChannels = info.TranscodingMaxAudioChannels;
-
- if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
- {
- SubtitleStreamIndex = info.SubtitleStreamIndex;
- }
- StreamOptions = info.StreamOptions;
- }
- }
-
- // For now until api and media encoding layers are unified
- public class BaseEncodingJobOptions
- {
- [ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool EnableAutoStreamCopy { get; set; }
-
- public bool AllowVideoStreamCopy { get; set; }
- public bool AllowAudioStreamCopy { get; set; }
- public bool BreakOnNonKeyFrames { get; set; }
-
- /// <summary>
- /// Gets or sets the audio sample rate.
- /// </summary>
- /// <value>The audio sample rate.</value>
- [ApiMember(Name = "AudioSampleRate", Description = "Optional. Specify a specific audio sample rate, e.g. 44100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AudioSampleRate { get; set; }
-
- public int? MaxAudioBitDepth { get; set; }
-
- /// <summary>
- /// Gets or sets the audio bit rate.
- /// </summary>
- /// <value>The audio bit rate.</value>
- [ApiMember(Name = "AudioBitRate", Description = "Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AudioBitRate { get; set; }
-
- /// <summary>
- /// Gets or sets the audio channels.
- /// </summary>
- /// <value>The audio channels.</value>
- [ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AudioChannels { get; set; }
-
- [ApiMember(Name = "MaxAudioChannels", Description = "Optional. Specify a maximum number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxAudioChannels { get; set; }
-
- [ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool Static { get; set; }
-
- /// <summary>
- /// Gets or sets the profile.
- /// </summary>
- /// <value>The profile.</value>
- [ApiMember(Name = "Profile", Description = "Optional. Specify a specific h264 profile, e.g. main, baseline, high.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string Profile { get; set; }
-
- /// <summary>
- /// Gets or sets the level.
- /// </summary>
- /// <value>The level.</value>
- [ApiMember(Name = "Level", Description = "Optional. Specify a level for the h264 profile, e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string Level { get; set; }
-
- /// <summary>
- /// Gets or sets the framerate.
- /// </summary>
- /// <value>The framerate.</value>
- [ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
- public float? Framerate { get; set; }
-
- [ApiMember(Name = "MaxFramerate", Description = "Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
- public float? MaxFramerate { get; set; }
-
- [ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool CopyTimestamps { get; set; }
-
- /// <summary>
- /// Gets or sets the start time ticks.
- /// </summary>
- /// <value>The start time ticks.</value>
- [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; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- [ApiMember(Name = "Width", Description = "Optional. The fixed horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- [ApiMember(Name = "Height", Description = "Optional. The fixed vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width of the max.
- /// </summary>
- /// <value>The width of the max.</value>
- [ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxWidth { get; set; }
-
- /// <summary>
- /// Gets or sets the height of the max.
- /// </summary>
- /// <value>The height of the max.</value>
- [ApiMember(Name = "MaxHeight", Description = "Optional. The maximum vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxHeight { get; set; }
-
- /// <summary>
- /// Gets or sets the video bit rate.
- /// </summary>
- /// <value>The video bit rate.</value>
- [ApiMember(Name = "VideoBitRate", Description = "Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? VideoBitRate { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the subtitle stream.
- /// </summary>
- /// <value>The index of the subtitle stream.</value>
- [ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? SubtitleStreamIndex { get; set; }
-
- [ApiMember(Name = "SubtitleMethod", Description = "Optional. Specify the subtitle delivery method.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public SubtitleDeliveryMethod SubtitleMethod { get; set; }
-
- [ApiMember(Name = "MaxRefFrames", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxRefFrames { get; set; }
-
- [ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxVideoBitDepth { get; set; }
- public bool RequireAvc { get; set; }
- public bool DeInterlace { get; set; }
- public bool RequireNonAnamorphic { get; set; }
- public int? TranscodingMaxAudioChannels { get; set; }
- public int? CpuCoreLimit { get; set; }
- public string OutputContainer { get; set; }
- public string LiveStreamId { get; set; }
-
- /// <summary>
- /// Gets or sets the video codec.
- /// </summary>
- /// <value>The video codec.</value>
- [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string VideoCodec { get; set; }
-
- public string SubtitleCodec { get; set; }
-
- public string TranscodeReasons { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the audio stream.
- /// </summary>
- /// <value>The index of the audio stream.</value>
- [ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AudioStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the video stream.
- /// </summary>
- /// <value>The index of the video stream.</value>
- [ApiMember(Name = "VideoStreamIndex", Description = "Optional. The index of the video stream to use. If omitted the first video stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? VideoStreamIndex { get; set; }
-
- public EncodingContext Context { get; set; }
-
- public void SetOption(string qualifier, string name, string value)
- {
- SetOption(qualifier + "-" + name, value);
- }
-
- public Dictionary<string, string> StreamOptions { get; set; }
-
- public void SetOption(string name, string value)
- {
- StreamOptions[name] = value;
- }
-
- public string GetOption(string qualifier, string name)
- {
- return GetOption(qualifier + "-" + name);
- }
-
- public string GetOption(string name)
- {
- string value;
- if (StreamOptions.TryGetValue(name, out value))
- {
- return value;
- }
-
- return null;
- }
-
- public BaseEncodingJobOptions()
- {
- EnableAutoStreamCopy = true;
- AllowVideoStreamCopy = true;
- AllowAudioStreamCopy = true;
- Context = EncodingContext.Streaming;
- StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
deleted file mode 100644
index 7d50efd5e..000000000
--- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Controller.Providers;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public interface IEncodingManager
- {
- /// <summary>
- /// Refreshes the chapter images.
- /// </summary>
- Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
deleted file mode 100644
index 2712380c7..000000000
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- /// <summary>
- /// Interface IMediaEncoder
- /// </summary>
- public interface IMediaEncoder : ITranscoderSupport
- {
- string EncoderLocationType { get; }
-
- /// <summary>
- /// Gets the encoder path.
- /// </summary>
- /// <value>The encoder path.</value>
- string EncoderPath { get; }
-
- /// <summary>
- /// Supportses the decoder.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool SupportsDecoder(string decoder);
-
- /// <summary>
- /// Extracts the audio image.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="imageStreamIndex">Index of the image stream.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Stream}.</returns>
- Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
-
- /// <summary>
- /// Extracts the video image.
- /// </summary>
- 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, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
-
- /// <summary>
- /// Extracts the video images on interval.
- /// </summary>
- Task ExtractVideoImagesOnInterval(string[] inputFiles,
- string container,
- MediaStream videoStream,
- MediaProtocol protocol,
- Video3DFormat? threedFormat,
- TimeSpan interval,
- string targetDirectory,
- string filenamePrefix,
- int? maxWidth,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the media info.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <returns>System.String.</returns>
- string GetInputArgument(string[] inputFiles, MediaProtocol protocol);
-
- /// <summary>
- /// Gets the time parameter.
- /// </summary>
- /// <param name="ticks">The ticks.</param>
- /// <returns>System.String.</returns>
- string GetTimeParameter(long ticks);
-
- /// <summary>
- /// Encodes the audio.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<string> EncodeAudio(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Encodes the video.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;System.String&gt;.</returns>
- Task<string> EncodeVideo(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken);
-
- Task ConvertImage(string inputPath, string outputPath);
-
- /// <summary>
- /// Escapes the subtitle filter path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- string EscapeSubtitleFilterPath(string path);
-
- Task Init();
-
- void UpdateEncoderPath(string path, string pathType);
- bool SupportsEncoder(string encoder);
-
- string[] GetPlayableStreamFileNames(string path, VideoType videoType);
- IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber);
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
deleted file mode 100644
index 44489cbf5..000000000
--- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Model.MediaInfo;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public interface ISubtitleEncoder
- {
- /// <summary>
- /// Gets the subtitles.
- /// </summary>
- /// <returns>Task{Stream}.</returns>
- Task<Stream> GetSubtitles(string itemId,
- string mediaSourceId,
- int subtitleStreamIndex,
- string outputFormat,
- long startTimeTicks,
- long? endTimeTicks,
- bool preserveOriginalTimestamps,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the subtitle language encoding parameter.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>System.String.</returns>
- Task<string> GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs
deleted file mode 100644
index a8d1e5a0f..000000000
--- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class ImageEncodingOptions
- {
- public string InputPath { get; set; }
-
- public int? Width { get; set; }
-
- public int? Height { get; set; }
-
- public int? MaxWidth { get; set; }
-
- public int? MaxHeight { get; set; }
-
- public int? Quality { get; set; }
-
- public string Format { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
deleted file mode 100644
index 70e4db84f..000000000
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- /// <summary>
- /// Class MediaEncoderHelpers
- /// </summary>
- public static class MediaEncoderHelpers
- {
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="videoPath">The video path.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="isoMount">The iso mount.</param>
- /// <param name="playableStreamFileNames">The playable stream file names.</param>
- /// <returns>System.String[][].</returns>
- public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, MediaProtocol protocol, IIsoMount isoMount, string[] playableStreamFileNames)
- {
- if (playableStreamFileNames.Length > 0)
- {
- if (isoMount == null)
- {
- return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames);
- }
- return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames);
- }
-
- return new[] {videoPath};
- }
-
- private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, string[] filenames)
- {
- if (filenames.Length == 0)
- {
- return new string[]{};
- }
-
- var allFiles = fileSystem
- .GetFilePaths(rootPath, true)
- .ToArray();
-
- return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
- .Where(f => !string.IsNullOrEmpty(f))
- .ToArray();
- }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
deleted file mode 100644
index 929f4e649..000000000
--- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class MediaInfoRequest
- {
- public string InputPath { get; set; }
- public MediaProtocol Protocol { get; set; }
- public bool ExtractChapters { get; set; }
- public DlnaProfileType MediaType { get; set; }
- public IIsoMount MountedIso { get; set; }
- public VideoType VideoType { get; set; }
- public string[] PlayableStreamFileNames { get; set; }
- public int AnalyzeDurationMs { get; set; }
-
- public MediaInfoRequest()
- {
- PlayableStreamFileNames = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
deleted file mode 100644
index ecbfaecea..000000000
--- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes
- {
- public static IAuthService AuthService { get; set; }
-
- /// <summary>
- /// Gets or sets the roles.
- /// </summary>
- /// <value>The roles.</value>
- public string Roles { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [escape parental control].
- /// </summary>
- /// <value><c>true</c> if [escape parental control]; otherwise, <c>false</c>.</value>
- public bool EscapeParentalControl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [allow before startup wizard].
- /// </summary>
- /// <value><c>true</c> if [allow before startup wizard]; otherwise, <c>false</c>.</value>
- public bool AllowBeforeStartupWizard { get; set; }
-
- public bool AllowLocal { get; set; }
-
- /// <summary>
- /// The request filter is executed before the service.
- /// </summary>
- /// <param name="request">The http request wrapper</param>
- /// <param name="response">The http response wrapper</param>
- /// <param name="requestDto">The request DTO</param>
- public void RequestFilter(IRequest request, IResponse response, object requestDto)
- {
- AuthService.Authenticate(request, this);
- }
-
- /// <summary>
- /// Order in which Request Filters are executed.
- /// &lt;0 Executed before global request filters
- /// &gt;0 Executed after global request filters
- /// </summary>
- /// <value>The priority.</value>
- public int Priority
- {
- get { return 0; }
- }
-
- public string[] GetRoles()
- {
- return (Roles ?? string.Empty).Split(new []{ ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
- }
-
- public interface IAuthenticationAttributes
- {
- bool EscapeParentalControl { get; }
- bool AllowBeforeStartupWizard { get; }
- bool AllowLocal { get; }
-
- string[] GetRoles();
- }
-}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
deleted file mode 100644
index d7dcb60f0..000000000
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-
-namespace MediaBrowser.Controller.Net
-{
- public class AuthorizationInfo
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- public string DeviceId { get; set; }
- /// <summary>
- /// Gets or sets the device.
- /// </summary>
- /// <value>The device.</value>
- public string Device { get; set; }
- /// <summary>
- /// Gets or sets the client.
- /// </summary>
- /// <value>The client.</value>
- public string Client { get; set; }
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
- /// <summary>
- /// Gets or sets the token.
- /// </summary>
- /// <value>The token.</value>
- public string Token { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
deleted file mode 100644
index 17b82b3ca..000000000
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ /dev/null
@@ -1,328 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Threading;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received
- /// </summary>
- /// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam>
- /// <typeparam name="TStateType">The type of the T state type.</typeparam>
- public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable
- where TStateType : WebSocketListenerState, new()
- where TReturnDataType : class
- {
- /// <summary>
- /// The _active connections
- /// </summary>
- protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>> ActiveConnections =
- new List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>>();
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- protected abstract string Name { get; }
-
- /// <summary>
- /// Gets the data to send.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>Task{`1}.</returns>
- protected abstract Task<TReturnDataType> GetDataToSend(TStateType state, CancellationToken cancellationToken);
-
- /// <summary>
- /// The logger
- /// </summary>
- protected ILogger Logger;
-
- protected ITimerFactory TimerFactory { get; private set; }
-
- protected BasePeriodicWebSocketListener(ILogger logger, ITimerFactory timerFactory)
- {
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- Logger = logger;
- TimerFactory = timerFactory;
- }
-
- /// <summary>
- /// The null task result
- /// </summary>
- protected Task NullTaskResult = Task.FromResult(true);
-
- /// <summary>
- /// Processes the message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>Task.</returns>
- public Task ProcessMessage(WebSocketMessageInfo message)
- {
- if (message == null)
- {
- throw new ArgumentNullException("message");
- }
-
- if (string.Equals(message.MessageType, Name + "Start", StringComparison.OrdinalIgnoreCase))
- {
- Start(message);
- }
-
- if (string.Equals(message.MessageType, Name + "Stop", StringComparison.OrdinalIgnoreCase))
- {
- Stop(message);
- }
-
- return NullTaskResult;
- }
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- protected virtual bool SendOnTimer
- {
- get
- {
- return false;
- }
- }
-
- protected virtual void ParseMessageParams(string[] values)
- {
-
- }
-
- /// <summary>
- /// Starts sending messages over a web socket
- /// </summary>
- /// <param name="message">The message.</param>
- private void Start(WebSocketMessageInfo message)
- {
- var vals = message.Data.Split(',');
-
- var dueTimeMs = long.Parse(vals[0], UsCulture);
- var periodMs = long.Parse(vals[1], UsCulture);
-
- if (vals.Length > 2)
- {
- ParseMessageParams(vals.Skip(2).ToArray());
- }
-
- var cancellationTokenSource = new CancellationTokenSource();
-
- Logger.Debug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
-
- var timer = SendOnTimer ?
- TimerFactory.Create(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
- null;
-
- var state = new TStateType
- {
- IntervalMs = periodMs,
- InitialDelayMs = dueTimeMs
- };
-
- lock (ActiveConnections)
- {
- ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>(message.Connection, cancellationTokenSource, timer, state));
- }
-
- if (timer != null)
- {
- timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
- }
- }
-
- /// <summary>
- /// Timers the callback.
- /// </summary>
- /// <param name="state">The state.</param>
- private void TimerCallback(object state)
- {
- var connection = (IWebSocketConnection)state;
-
- Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> tuple;
-
- lock (ActiveConnections)
- {
- tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection);
- }
-
- if (tuple == null)
- {
- return;
- }
-
- if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested)
- {
- DisposeConnection(tuple);
- return;
- }
-
- SendData(tuple);
- }
-
- protected void SendData(bool force)
- {
- List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>> tuples;
-
- lock (ActiveConnections)
- {
- tuples = ActiveConnections
- .Where(c =>
- {
- if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested)
- {
- var state = c.Item4;
-
- if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs)
- {
- return true;
- }
- }
-
- return false;
- })
- .ToList();
- }
-
- foreach (var tuple in tuples)
- {
- SendData(tuple);
- }
- }
-
- private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> tuple)
- {
- var connection = tuple.Item1;
-
- try
- {
- var state = tuple.Item4;
-
- var cancellationToken = tuple.Item2.Token;
-
- var data = await GetDataToSend(state, cancellationToken).ConfigureAwait(false);
-
- if (data != null)
- {
- await connection.SendAsync(new WebSocketMessage<TReturnDataType>
- {
- MessageType = Name,
- Data = data
-
- }, cancellationToken).ConfigureAwait(false);
-
- state.DateLastSendUtc = DateTime.UtcNow;
- }
- }
- catch (OperationCanceledException)
- {
- if (tuple.Item2.IsCancellationRequested)
- {
- DisposeConnection(tuple);
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message {0}", ex, Name);
- DisposeConnection(tuple);
- }
- }
-
- /// <summary>
- /// Stops sending messages over a web socket
- /// </summary>
- /// <param name="message">The message.</param>
- private void Stop(WebSocketMessageInfo message)
- {
- lock (ActiveConnections)
- {
- var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection);
-
- if (connection != null)
- {
- DisposeConnection(connection);
- }
- }
- }
-
- /// <summary>
- /// Disposes the connection.
- /// </summary>
- /// <param name="connection">The connection.</param>
- private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> connection)
- {
- Logger.Debug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
-
- var timer = connection.Item3;
-
- if (timer != null)
- {
- try
- {
- timer.Dispose();
- }
- catch (ObjectDisposedException)
- {
-
- }
- }
-
- try
- {
- connection.Item2.Cancel();
- connection.Item2.Dispose();
- }
- catch (ObjectDisposedException)
- {
-
- }
-
- ActiveConnections.Remove(connection);
- }
-
- /// <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)
- {
- lock (ActiveConnections)
- {
- foreach (var connection in ActiveConnections.ToList())
- {
- DisposeConnection(connection);
- }
- }
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
-
- public class WebSocketListenerState
- {
- public DateTime DateLastSendUtc { get; set; }
- public long InitialDelayMs { get; set; }
- public long IntervalMs { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
deleted file mode 100644
index 361320250..000000000
--- a/MediaBrowser.Controller/Net/IAuthService.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public interface IAuthService
- {
- void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IAuthorizationContext.cs b/MediaBrowser.Controller/Net/IAuthorizationContext.cs
deleted file mode 100644
index 5a9d0aa30..000000000
--- a/MediaBrowser.Controller/Net/IAuthorizationContext.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public interface IAuthorizationContext
- {
- /// <summary>
- /// Gets the authorization information.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <returns>AuthorizationInfo.</returns>
- AuthorizationInfo GetAuthorizationInfo(object requestContext);
-
- /// <summary>
- /// Gets the authorization information.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <returns>AuthorizationInfo.</returns>
- AuthorizationInfo GetAuthorizationInfo(IRequest requestContext);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHasResultFactory.cs b/MediaBrowser.Controller/Net/IHasResultFactory.cs
deleted file mode 100644
index 03144e4b8..000000000
--- a/MediaBrowser.Controller/Net/IHasResultFactory.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IHasResultFactory
- /// Services that require a ResultFactory should implement this
- /// </summary>
- public interface IHasResultFactory : IRequiresRequest
- {
- /// <summary>
- /// Gets or sets the result factory.
- /// </summary>
- /// <value>The result factory.</value>
- IHttpResultFactory ResultFactory { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
deleted file mode 100644
index b88524691..000000000
--- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IHttpResultFactory
- /// </summary>
- public interface IHttpResultFactory
- {
- /// <summary>
- /// Gets the result.
- /// </summary>
- /// <param name="content">The content.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- object GetResult(object content, string contentType, IDictionary<string,string> responseHeaders = null);
-
- object GetRedirectResult(string url);
-
- /// <summary>
- /// Gets the optimized result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="result">The result.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- object GetOptimizedResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
- where T : class;
-
- /// <summary>
- /// Gets the optimized result using cache.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory function that creates the response object.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- object GetOptimizedResultUsingCache<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, IDictionary<string, string> responseHeaders = null)
- where T : class;
-
- /// <summary>
- /// Gets the cached result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- object GetCachedResult<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, string contentType, IDictionary<string, string> responseHeaders = null)
- where T : class;
-
- /// <summary>
- /// Gets the static result.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
- /// <returns>System.Object.</returns>
- Task<object> GetStaticResult(IRequest requestContext,
- Guid cacheKey,
- DateTime? lastDateModified,
- TimeSpan? cacheDuration,
- string contentType, Func<Task<Stream>> factoryFn,
- IDictionary<string, string> responseHeaders = null,
- bool isHeadRequest = false);
-
- /// <summary>
- /// Gets the static result.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.Object.</returns>
- Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options);
-
- /// <summary>
- /// Gets the static file result.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="path">The path.</param>
- /// <param name="fileShare">The file share.</param>
- /// <returns>System.Object.</returns>
- Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShareMode fileShare = FileShareMode.Read);
-
- /// <summary>
- /// Gets the static file result.
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.Object.</returns>
- Task<object> GetStaticFileResult(IRequest requestContext,
- StaticFileResultOptions options);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
deleted file mode 100644
index f41572b45..000000000
--- a/MediaBrowser.Controller/Net/IHttpServer.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IHttpServer
- /// </summary>
- public interface IHttpServer : IDisposable
- {
- /// <summary>
- /// Gets the URL prefix.
- /// </summary>
- /// <value>The URL prefix.</value>
- string[] UrlPrefixes { get; }
-
- /// <summary>
- /// Starts the specified server name.
- /// </summary>
- /// <param name="urlPrefixes">The URL prefixes.</param>
- void StartServer(string[] urlPrefixes);
-
- /// <summary>
- /// Stops this instance.
- /// </summary>
- void Stop();
-
- /// <summary>
- /// Occurs when [web socket connected].
- /// </summary>
- event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
-
- /// <summary>
- /// Occurs when [web socket connecting].
- /// </summary>
- event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting;
-
- /// <summary>
- /// Inits this instance.
- /// </summary>
- void Init(IEnumerable<IService> services);
-
- /// <summary>
- /// If set, all requests will respond with this message
- /// </summary>
- string GlobalResponse { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Net/IServerManager.cs b/MediaBrowser.Controller/Net/IServerManager.cs
deleted file mode 100644
index a84c48c5b..000000000
--- a/MediaBrowser.Controller/Net/IServerManager.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using MediaBrowser.Model.Events;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IServerManager
- /// </summary>
- public interface IServerManager : IDisposable
- {
- /// <summary>
- /// Starts this instance.
- /// </summary>
- /// <param name="urlPrefixes">The URL prefixes.</param>
- void Start(string[] urlPrefixes);
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="data">The data.</param>
- /// <returns>Task.</returns>
- void SendWebSocketMessage<T>(string messageType, T data);
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction);
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType</exception>
- Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken);
-
- /// <summary>
- /// Adds the web socket listeners.
- /// </summary>
- /// <param name="listeners">The listeners.</param>
- void AddWebSocketListeners(IEnumerable<IWebSocketListener> listeners);
-
- /// <summary>
- /// Gets the web socket connections.
- /// </summary>
- /// <value>The web socket connections.</value>
- IEnumerable<IWebSocketConnection> WebSocketConnections { get; }
-
- /// <summary>
- /// Occurs when [web socket connected].
- /// </summary>
- event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
deleted file mode 100644
index 213a66dac..000000000
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Session;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public interface ISessionContext
- {
- Task<SessionInfo> GetSession(object requestContext);
- Task<User> GetUser(object requestContext);
-
- Task<SessionInfo> GetSession(IRequest requestContext);
- Task<User> GetUser(IRequest requestContext);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
deleted file mode 100644
index dad238891..000000000
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using MediaBrowser.Model.Net;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public interface IWebSocketConnection : IDisposable
- {
- /// <summary>
- /// Occurs when [closed].
- /// </summary>
- event EventHandler<EventArgs> Closed;
-
- /// <summary>
- /// Gets the id.
- /// </summary>
- /// <value>The id.</value>
- Guid Id { get; }
-
- /// <summary>
- /// Gets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- DateTime LastActivityDate { get; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- string Url { get; set; }
- /// <summary>
- /// Gets or sets the query string.
- /// </summary>
- /// <value>The query string.</value>
- QueryParamCollection QueryString { get; set; }
-
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
- Action<WebSocketMessageInfo> OnReceive { get; set; }
-
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
- WebSocketState State { get; }
-
- /// <summary>
- /// Gets the remote end point.
- /// </summary>
- /// <value>The remote end point.</value>
- string RemoteEndPoint { get; }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="message">The message.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">message</exception>
- Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <param name="text">The text.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">buffer</exception>
- Task SendAsync(string text, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs
deleted file mode 100644
index 29698c1a4..000000000
--- a/MediaBrowser.Controller/Net/IWebSocketListener.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- ///This is an interface for listening to messages coming through a web socket connection
- /// </summary>
- public interface IWebSocketListener
- {
- /// <summary>
- /// Processes the message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>Task.</returns>
- Task ProcessMessage(WebSocketMessageInfo message);
- }
-}
diff --git a/MediaBrowser.Controller/Net/LoggedAttribute.cs b/MediaBrowser.Controller/Net/LoggedAttribute.cs
deleted file mode 100644
index eb57392e2..000000000
--- a/MediaBrowser.Controller/Net/LoggedAttribute.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
-using System;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- public class LoggedAttribute : IRequestFilter
- {
- public LoggedAttribute(ILogger logger, IUserManager userManager, ISessionManager sessionManager, IAuthorizationContext authorizationContext)
- {
- Logger = logger;
- UserManager = userManager;
- SessionManager = sessionManager;
- AuthorizationContext = authorizationContext;
- }
-
- public ILogger Logger { get; private set; }
- public IUserManager UserManager { get; private set; }
- public ISessionManager SessionManager { get; private set; }
- public IAuthorizationContext AuthorizationContext { get; private set; }
-
- /// <summary>
- /// The request filter is executed before the service.
- /// </summary>
- /// <param name="request">The http request wrapper</param>
- /// <param name="response">The http response wrapper</param>
- /// <param name="requestDto">The request DTO</param>
- public void Filter(IRequest request, IResponse response, object requestDto)
- {
- //This code is executed before the service
- var auth = AuthorizationContext.GetAuthorizationInfo(request);
-
- if (auth != null)
- {
- User user = null;
-
- if (!string.IsNullOrWhiteSpace(auth.UserId))
- {
- var userId = auth.UserId;
-
- user = UserManager.GetUserById(userId);
- }
-
- string deviceId = auth.DeviceId;
- string device = auth.Device;
- string client = auth.Client;
- string version = auth.Version;
-
- if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
- {
- var remoteEndPoint = request.RemoteIp;
-
- SessionManager.LogSessionActivity(client, version, deviceId, device, remoteEndPoint, user);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs
deleted file mode 100644
index b251ab9a9..000000000
--- a/MediaBrowser.Controller/Net/SecurityException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Net
-{
- public class SecurityException : Exception
- {
- public SecurityException(string message)
- : base(message)
- {
-
- }
-
- public SecurityExceptionType SecurityExceptionType { get; set; }
- }
-
- public enum SecurityExceptionType
- {
- Unauthenticated = 0,
- ParentalControl = 1
- }
-}
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
deleted file mode 100644
index 3064cbf10..000000000
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Net
-{
- public class StaticResultOptions
- {
- public string ContentType { get; set; }
- public TimeSpan? CacheDuration { get; set; }
- public DateTime? DateLastModified { get; set; }
- public Guid CacheKey { get; set; }
-
- public Func<Task<Stream>> ContentFactory { get; set; }
-
- public bool IsHeadRequest { get; set; }
-
- public IDictionary<string, string> ResponseHeaders { get; set; }
-
- public Action OnComplete { get; set; }
- public Action OnError { get; set; }
-
- public string Path { get; set; }
-
- public FileShareMode FileShare { get; set; }
-
- public StaticResultOptions()
- {
- ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- FileShare = FileShareMode.Read;
- }
- }
-
- public class StaticFileResultOptions : StaticResultOptions
- {
- }
-}
diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
deleted file mode 100644
index 0ad80d374..000000000
--- a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using System;
-using System.Collections.Specialized;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Class WebSocketConnectEventArgs
- /// </summary>
- public class WebSocketConnectEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
- /// <summary>
- /// Gets or sets the query string.
- /// </summary>
- /// <value>The query string.</value>
- public QueryParamCollection QueryString { get; set; }
- /// <summary>
- /// Gets or sets the web socket.
- /// </summary>
- /// <value>The web socket.</value>
- public IWebSocket WebSocket { get; set; }
- /// <summary>
- /// Gets or sets the endpoint.
- /// </summary>
- /// <value>The endpoint.</value>
- public string Endpoint { get; set; }
- }
-
- public class WebSocketConnectingEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
- /// <summary>
- /// Gets or sets the endpoint.
- /// </summary>
- /// <value>The endpoint.</value>
- public string Endpoint { get; set; }
- /// <summary>
- /// Gets or sets the query string.
- /// </summary>
- /// <value>The query string.</value>
- public QueryParamCollection QueryString { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [allow connection].
- /// </summary>
- /// <value><c>true</c> if [allow connection]; otherwise, <c>false</c>.</value>
- public bool AllowConnection { get; set; }
-
- public WebSocketConnectingEventArgs()
- {
- QueryString = new QueryParamCollection();
- AllowConnection = true;
- }
- }
-
-}
diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
deleted file mode 100644
index 332f16420..000000000
--- a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using MediaBrowser.Model.Net;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Class WebSocketMessageInfo
- /// </summary>
- public class WebSocketMessageInfo : WebSocketMessage<string>
- {
- /// <summary>
- /// Gets or sets the connection.
- /// </summary>
- /// <value>The connection.</value>
- public IWebSocketConnection Connection { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs
deleted file mode 100644
index 68cfd6ff1..000000000
--- a/MediaBrowser.Controller/Notifications/INotificationManager.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using MediaBrowser.Model.Notifications;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Notifications
-{
- public interface INotificationManager
- {
- /// <summary>
- /// Sends the notification.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendNotification(NotificationRequest request, CancellationToken cancellationToken);
-
- Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="services">The services.</param>
- /// <param name="notificationTypeFactories">The notification type factories.</param>
- void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories);
-
- /// <summary>
- /// Gets the notification types.
- /// </summary>
- /// <returns>IEnumerable{NotificationTypeInfo}.</returns>
- List<NotificationTypeInfo> GetNotificationTypes();
-
- /// <summary>
- /// Gets the notification services.
- /// </summary>
- /// <returns>IEnumerable{NotificationServiceInfo}.</returns>
- IEnumerable<NotificationServiceInfo> GetNotificationServices();
- }
-}
diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs
deleted file mode 100644
index b1e313b87..000000000
--- a/MediaBrowser.Controller/Notifications/INotificationService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Notifications
-{
- public interface INotificationService
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Sends the notification.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendNotification(UserNotification request, CancellationToken cancellationToken);
-
- /// <summary>
- /// Determines whether [is enabled for user] [the specified user identifier].
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns><c>true</c> if [is enabled for user] [the specified user identifier]; otherwise, <c>false</c>.</returns>
- bool IsEnabledForUser(User user);
- }
-}
diff --git a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs
deleted file mode 100644
index bf92aae2d..000000000
--- a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using MediaBrowser.Model.Notifications;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Notifications
-{
- public interface INotificationTypeFactory
- {
- /// <summary>
- /// Gets the notification types.
- /// </summary>
- /// <returns>IEnumerable{NotificationTypeInfo}.</returns>
- IEnumerable<NotificationTypeInfo> GetNotificationTypes();
- }
-}
diff --git a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs
deleted file mode 100644
index cd587a509..000000000
--- a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using MediaBrowser.Model.Notifications;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Notifications
-{
- /// <summary>
- /// Interface INotificationsRepository
- /// </summary>
- public interface INotificationsRepository
- {
- /// <summary>
- /// Occurs when [notification added].
- /// </summary>
- event EventHandler<NotificationUpdateEventArgs> NotificationAdded;
- /// <summary>
- /// Occurs when [notifications marked read].
- /// </summary>
- event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
-
- /// <summary>
- /// Gets the notifications.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>NotificationResult.</returns>
- NotificationResult GetNotifications(NotificationQuery query);
-
- /// <summary>
- /// Adds the notification.
- /// </summary>
- /// <param name="notification">The notification.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task AddNotification(Notification notification, CancellationToken cancellationToken);
-
- /// <summary>
- /// Marks the read.
- /// </summary>
- /// <param name="notificationIdList">The notification id list.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="isRead">if set to <c>true</c> [is read].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task MarkRead(IEnumerable<string> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken);
-
- /// <summary>
- /// Marks all read.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="isRead">if set to <c>true</c> [is read].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task MarkAllRead(string userId, bool isRead, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the notifications summary.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>NotificationsSummary.</returns>
- NotificationsSummary GetNotificationsSummary(string userId);
- }
-}
diff --git a/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs b/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs
deleted file mode 100644
index d8a6634df..000000000
--- a/MediaBrowser.Controller/Notifications/NotificationUpdateEventArgs.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Notifications;
-using System;
-
-namespace MediaBrowser.Controller.Notifications
-{
- public class NotificationUpdateEventArgs : EventArgs
- {
- public Notification Notification { get; set; }
- }
-
- public class NotificationReadEventArgs : EventArgs
- {
- public string[] IdList { get; set; }
- public string UserId { get; set; }
- public bool IsRead { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs
deleted file mode 100644
index d035a3995..000000000
--- a/MediaBrowser.Controller/Notifications/UserNotification.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Notifications;
-using System;
-
-namespace MediaBrowser.Controller.Notifications
-{
- public class UserNotification
- {
- public string Name { get; set; }
-
- public string Description { get; set; }
-
- public string Url { get; set; }
-
- public NotificationLevel Level { get; set; }
-
- public DateTime Date { get; set; }
-
- public User User { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs
deleted file mode 100644
index 25aba6bd9..000000000
--- a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Threading;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Interface IDisplayPreferencesRepository
- /// </summary>
- public interface IDisplayPreferencesRepository : IRepository
- {
- /// <summary>
- /// Saves display preferences for an item
- /// </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.</returns>
- void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Saves all display preferences for a user
- /// </summary>
- /// <param name="displayPreferences">The display preferences.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId,
- CancellationToken cancellationToken);
- /// <summary>
- /// Gets the display preferences.
- /// </summary>
- /// <param name="displayPreferencesId">The display preferences id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="client">The client.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client);
-
- /// <summary>
- /// Gets all display preferences for the given user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId);
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
deleted file mode 100644
index 4cb3e2bb6..000000000
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides an interface to implement an Item repository
- /// </summary>
- public interface IItemRepository : IRepository
- {
- /// <summary>
- /// Saves an item
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void SaveItem(BaseItem item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void DeleteItem(Guid id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the critic reviews.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <returns>Task{IEnumerable{ItemReview}}.</returns>
- List<ItemReview> GetCriticReviews(Guid itemId);
-
- /// <summary>
- /// Saves the critic reviews.
- /// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="criticReviews">The critic reviews.</param>
- void SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews);
-
- /// <summary>
- /// Saves the items.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void SaveItems(List<BaseItem> items, CancellationToken cancellationToken);
-
- void SaveImages(BaseItem item);
-
- /// <summary>
- /// Retrieves the item.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- BaseItem RetrieveItem(Guid id);
-
- /// <summary>
- /// Gets chapters for an item
- /// </summary>
- /// <param name="id"></param>
- /// <returns></returns>
- List<ChapterInfo> GetChapters(Guid id);
-
- /// <summary>
- /// Gets a single chapter for an item
- /// </summary>
- /// <param name="id"></param>
- /// <param name="index"></param>
- /// <returns></returns>
- ChapterInfo GetChapter(Guid id, int index);
-
- /// <summary>
- /// Saves the chapters.
- /// </summary>
- void SaveChapters(Guid id, List<ChapterInfo> chapters);
-
- /// <summary>
- /// Gets the media streams.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable{MediaStream}.</returns>
- List<MediaStream> GetMediaStreams(MediaStreamQuery query);
-
- /// <summary>
- /// Saves the media streams.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="streams">The streams.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the item ids.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>IEnumerable&lt;Guid&gt;.</returns>
- QueryResult<Guid> GetItemIds(InternalItemsQuery query);
- /// <summary>
- /// Gets the items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
- QueryResult<BaseItem> GetItems(InternalItemsQuery query);
-
- /// <summary>
- /// Gets the item ids list.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;Guid&gt;.</returns>
- List<Guid> GetItemIdsList(InternalItemsQuery query);
-
- /// <summary>
- /// Gets the people.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;PersonInfo&gt;.</returns>
- List<PersonInfo> GetPeople(InternalPeopleQuery query);
-
- /// <summary>
- /// Updates the people.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="people">The people.</param>
- void UpdatePeople(Guid itemId, List<PersonInfo> people);
-
- /// <summary>
- /// Gets the people names.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;System.String&gt;.</returns>
- List<string> GetPeopleNames(InternalPeopleQuery query);
-
- /// <summary>
- /// Gets the item ids with path.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
- List<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
-
- /// <summary>
- /// Gets the item list.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>List&lt;BaseItem&gt;.</returns>
- List<BaseItem> GetItemList(InternalItemsQuery query);
-
- /// <summary>
- /// Updates the inherited values.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- void UpdateInheritedValues(CancellationToken cancellationToken);
-
- int GetCount(InternalItemsQuery query);
-
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
-
- List<string> GetGameGenreNames();
- List<string> GetMusicGenreNames();
- List<string> GetStudioNames();
- List<string> GetGenreNames();
- List<string> GetAllArtistNames();
- }
-}
-
diff --git a/MediaBrowser.Controller/Persistence/IRepository.cs b/MediaBrowser.Controller/Persistence/IRepository.cs
deleted file mode 100644
index 2340ca646..000000000
--- a/MediaBrowser.Controller/Persistence/IRepository.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides a base interface for all the repository interfaces
- /// </summary>
- public interface IRepository : IDisposable
- {
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
deleted file mode 100644
index f79a3a9fc..000000000
--- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Threading;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides an interface to implement a UserData repository
- /// </summary>
- public interface IUserDataRepository : IRepository
- {
- /// <summary>
- /// Saves the user data.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="key">The key.</param>
- /// <param name="userData">The user data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the user data.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="key">The key.</param>
- /// <returns>Task{UserItemData}.</returns>
- UserItemData GetUserData(Guid userId, string key);
-
- UserItemData GetUserData(Guid userId, List<string> keys);
-
- /// <summary>
- /// Return all user data associated with the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <returns></returns>
- List<UserItemData> GetAllUserData(Guid userId);
-
- /// <summary>
- /// Save all user data associated with the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <param name="userData"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken);
-
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/IUserRepository.cs b/MediaBrowser.Controller/Persistence/IUserRepository.cs
deleted file mode 100644
index 721ddb7e3..000000000
--- a/MediaBrowser.Controller/Persistence/IUserRepository.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides an interface to implement a User repository
- /// </summary>
- public interface IUserRepository : IRepository
- {
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void DeleteUser(User user, CancellationToken cancellationToken);
-
- /// <summary>
- /// Saves the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void SaveUser(User user, CancellationToken cancellationToken);
-
- /// <summary>
- /// Retrieves all users.
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- IEnumerable<User> RetrieveAllUsers();
- }
-}
diff --git a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs b/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs
deleted file mode 100644
index 10985f57d..000000000
--- a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-
-namespace MediaBrowser.Controller.Persistence
-{
- public class MediaStreamQuery
- {
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public MediaStreamType? Type { get; set; }
-
- /// <summary>
- /// Gets or sets the index.
- /// </summary>
- /// <value>The index.</value>
- public int? Index { get; set; }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public Guid ItemId { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
deleted file mode 100644
index a9336d8e3..000000000
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Playlists;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Playlists
-{
- public interface IPlaylistManager
- {
- /// <summary>
- /// Gets the playlists.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>IEnumerable&lt;Playlist&gt;.</returns>
- IEnumerable<Playlist> GetPlaylists(string userId);
-
- /// <summary>
- /// Creates the playlist.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task&lt;Playlist&gt;.</returns>
- Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options);
-
- /// <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 playlists folder.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Folder.</returns>
- Folder GetPlaylistsFolder(string userId);
-
- /// <summary>
- /// Moves the item.
- /// </summary>
- /// <param name="playlistId">The playlist identifier.</param>
- /// <param name="entryId">The entry identifier.</param>
- /// <param name="newIndex">The new index.</param>
- /// <returns>Task.</returns>
- Task MoveItem(string playlistId, string entryId, int newIndex);
- }
-}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
deleted file mode 100644
index 071f8a096..000000000
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Providers;
-
-namespace MediaBrowser.Controller.Playlists
-{
- public class Playlist : Folder, IHasShares
- {
- public string OwnerUserId { get; set; }
-
- public List<Share> Shares { get; set; }
-
- public Playlist()
- {
- Shares = new List<Share>();
- }
-
- [IgnoreDataMember]
- protected override bool FilterLinkedChildrenPerUser
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsInheritedParentImages
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsPlayedStatus
- {
- get
- {
- return string.Equals(MediaType, "Video", StringComparison.OrdinalIgnoreCase);
- }
- }
-
- [IgnoreDataMember]
- public override bool AlwaysScanInternalMetadataPath
- {
- get
- {
- return true;
- }
- }
-
- [IgnoreDataMember]
- public override bool SupportsCumulativeRunTimeTicks
- {
- get
- {
- return true;
- }
- }
-
- public override double? GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
- {
- return true;
- }
-
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
- protected override List<BaseItem> LoadChildren()
- {
- // Save a trip to the database
- return new List<BaseItem>();
- }
-
- public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
- {
- return GetPlayableItems(user, new DtoOptions(true));
- }
-
- protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- return new List<BaseItem>();
- }
-
- public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
- {
- var items = GetPlayableItems(user, query.DtoOptions);
-
- if (query != null)
- {
- items = items.Where(i => UserViewBuilder.FilterItem(i, query)).ToList();
- }
-
- return items;
- }
-
- public IEnumerable<Tuple<LinkedChild, BaseItem>> GetManageableItems()
- {
- return GetLinkedChildrenInfos();
- }
-
- private List<BaseItem> GetPlayableItems(User user, DtoOptions options)
- {
- return GetPlaylistItems(MediaType, base.GetChildren(user, true), user, options);
- }
-
- public static List<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user, DtoOptions options)
- {
- if (user != null)
- {
- inputItems = inputItems.Where(i => i.IsVisible(user));
- }
-
- var list = new List<BaseItem>();
-
- foreach (var item in inputItems)
- {
- var playlistItems = GetPlaylistItems(item, user, playlistMediaType, options);
- list.AddRange(playlistItems);
- }
-
- return list;
- }
-
- private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem item, User user, string mediaType, DtoOptions options)
- {
- var musicGenre = item as MusicGenre;
- if (musicGenre != null)
- {
- return LibraryManager.GetItemList(new InternalItemsQuery(user)
- {
- Recursive = true,
- IncludeItemTypes = new[] { typeof(Audio).Name },
- GenreIds = new[] { musicGenre.Id.ToString("N") },
- OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- DtoOptions = options
- });
- }
-
- var musicArtist = item as MusicArtist;
- if (musicArtist != null)
- {
- return LibraryManager.GetItemList(new InternalItemsQuery(user)
- {
- Recursive = true,
- IncludeItemTypes = new[] { typeof(Audio).Name },
- ArtistIds = new[] { musicArtist.Id.ToString("N") },
- OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- DtoOptions = options
- });
- }
-
- var folder = item as Folder;
- if (folder != null)
- {
- var query = new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false,
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
- MediaTypes = new[] { mediaType },
- EnableTotalRecordCount = false,
- DtoOptions = options
- };
-
- return folder.GetItemList(query);
- }
-
- return new[] { item };
- }
-
- [IgnoreDataMember]
- public override bool IsPreSorted
- {
- get
- {
- return true;
- }
- }
-
- public string PlaylistMediaType { get; set; }
-
- [IgnoreDataMember]
- public override string MediaType
- {
- get
- {
- return PlaylistMediaType;
- }
- }
-
- public void SetMediaType(string value)
- {
- PlaylistMediaType = value;
- }
-
- public override bool IsVisible(User user)
- {
- var userId = user.Id.ToString("N");
-
- return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
- string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
- }
-
- public override bool IsVisibleStandalone(User user)
- {
- return IsVisible(user);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs
deleted file mode 100644
index d294107d7..000000000
--- a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.IO;
-using System.Reflection;
-
-namespace MediaBrowser.Controller.Plugins
-{
- public interface ILocalizablePlugin
- {
- Stream GetDictionary(string culture);
- }
-
- public static class LocalizablePluginHelper
- {
- public static Stream GetDictionary(Assembly assembly, string manifestPrefix, string culture)
- {
- // Find all dictionaries using GetManifestResourceNames, start start with the prefix
- // Return the one for the culture if exists, otherwise return the default
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs
deleted file mode 100644
index 5feaf798c..000000000
--- a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using MediaBrowser.Common.Plugins;
-using System.IO;
-
-namespace MediaBrowser.Controller.Plugins
-{
- /// <summary>
- /// Interface IConfigurationPage
- /// </summary>
- public interface IPluginConfigurationPage
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the type of the configuration page.
- /// </summary>
- /// <value>The type of the configuration page.</value>
- ConfigurationPageType ConfigurationPageType { get; }
-
- /// <summary>
- /// Gets the plugin.
- /// </summary>
- /// <value>The plugin.</value>
- IPlugin Plugin { get; }
-
- /// <summary>
- /// Gets the HTML stream.
- /// </summary>
- /// <returns>Stream.</returns>
- Stream GetHtmlStream();
- }
-
- /// <summary>
- /// Enum ConfigurationPageType
- /// </summary>
- public enum ConfigurationPageType
- {
- /// <summary>
- /// The plugin configuration
- /// </summary>
- PluginConfiguration,
- /// <summary>
- /// The none
- /// </summary>
- None
- }
-}
diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
deleted file mode 100644
index de6de8f84..000000000
--- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Plugins
-{
- /// <summary>
- /// Interface IServerEntryPoint
- /// </summary>
- public interface IServerEntryPoint : IDisposable
- {
- /// <summary>
- /// Runs this instance.
- /// </summary>
- void Run();
- }
-}
diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs
deleted file mode 100644
index 844b93f37..000000000
--- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,27 +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.Controller")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Controller")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[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)]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-// \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs
deleted file mode 100644
index 74feb4ea2..000000000
--- a/MediaBrowser.Controller/Providers/AlbumInfo.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class AlbumInfo : ItemLookupInfo
- {
- /// <summary>
- /// Gets or sets the album artist.
- /// </summary>
- /// <value>The album artist.</value>
- public string[] AlbumArtists { get; set; }
-
- /// <summary>
- /// Gets or sets the artist provider ids.
- /// </summary>
- /// <value>The artist provider ids.</value>
- public Dictionary<string, string> ArtistProviderIds { get; set; }
- public List<SongInfo> SongInfos { get; set; }
-
- public AlbumInfo()
- {
- ArtistProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- SongInfos = new List<SongInfo>();
- AlbumArtists = EmptyStringArray;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ArtistInfo.cs b/MediaBrowser.Controller/Providers/ArtistInfo.cs
deleted file mode 100644
index 8a4abd5c6..000000000
--- a/MediaBrowser.Controller/Providers/ArtistInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ArtistInfo : ItemLookupInfo
- {
- public List<SongInfo> SongInfos { get; set; }
-
- public ArtistInfo()
- {
- SongInfos = new List<SongInfo>();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/BookInfo.cs b/MediaBrowser.Controller/Providers/BookInfo.cs
deleted file mode 100644
index 52519bcb0..000000000
--- a/MediaBrowser.Controller/Providers/BookInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class BookInfo : ItemLookupInfo
- {
- public string SeriesName { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/BoxSetInfo.cs b/MediaBrowser.Controller/Providers/BoxSetInfo.cs
deleted file mode 100644
index f604231de..000000000
--- a/MediaBrowser.Controller/Providers/BoxSetInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class BoxSetInfo : ItemLookupInfo
- {
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
deleted file mode 100644
index d673198fd..000000000
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class DirectoryService : IDirectoryService
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
-
- private readonly Dictionary<string, FileSystemMetadata[]> _cache = new Dictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
-
- private readonly Dictionary<string, FileSystemMetadata> _fileCache = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
-
- private readonly Dictionary<string, List<string>> _filePathCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
-
- public DirectoryService(ILogger logger, IFileSystem fileSystem)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- }
-
- public DirectoryService(IFileSystem fileSystem)
- : this(new NullLogger(), fileSystem)
- {
- }
-
- public FileSystemMetadata[] GetFileSystemEntries(string path)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- FileSystemMetadata[] entries;
-
- if (!_cache.TryGetValue(path, out entries))
- {
- //_logger.Debug("Getting files for " + path);
-
- try
- {
- // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
- entries = _fileSystem.GetFileSystemEntries(path).ToArray();
- }
- catch (IOException)
- {
- entries = new FileSystemMetadata[] { };
- }
-
- //_cache.TryAdd(path, entries);
- _cache[path] = entries;
- }
-
- return entries;
- }
-
- public List<FileSystemMetadata> GetFiles(string path)
- {
- var list = new List<FileSystemMetadata>();
- var items = GetFileSystemEntries(path);
- foreach (var item in items)
- {
- if (!item.IsDirectory)
- {
- list.Add(item);
- }
- }
- return list;
- }
-
- public FileSystemMetadata GetFile(string path)
- {
- FileSystemMetadata file;
- if (!_fileCache.TryGetValue(path, out file))
- {
- file = _fileSystem.GetFileInfo(path);
-
- if (file != null && file.Exists)
- {
- //_fileCache.TryAdd(path, file);
- _fileCache[path] = file;
- }
- else
- {
- return null;
- }
- }
-
- return file;
- //return _fileSystem.GetFileInfo(path);
- }
-
- public List<string> GetFilePaths(string path)
- {
- return GetFilePaths(path, false);
- }
-
- public List<string> GetFilePaths(string path, bool clearCache)
- {
- List<string> result;
- if (clearCache || !_filePathCache.TryGetValue(path, out result))
- {
- result = _fileSystem.GetFilePaths(path).ToList();
-
- _filePathCache[path] = result;
- }
-
- return result;
- }
-
- }
-}
diff --git a/MediaBrowser.Controller/Providers/DynamicImageInfo.cs b/MediaBrowser.Controller/Providers/DynamicImageInfo.cs
deleted file mode 100644
index 14b4c6afb..000000000
--- a/MediaBrowser.Controller/Providers/DynamicImageInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class DynamicImageInfo
- {
- public string ImageId { get; set; }
- public ImageType Type { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
deleted file mode 100644
index d19a28a24..000000000
--- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.IO;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class DynamicImageResponse
- {
- public string Path { get; set; }
- public MediaProtocol Protocol { get; set; }
- public Stream Stream { get; set; }
- public ImageFormat Format { get; set; }
- public bool HasImage { get; set; }
-
- public void SetFormatFromMimeType(string mimeType)
- {
- if (mimeType.EndsWith("gif", StringComparison.OrdinalIgnoreCase))
- {
- Format = ImageFormat.Gif;
- }
- else if (mimeType.EndsWith("bmp", StringComparison.OrdinalIgnoreCase))
- {
- Format = ImageFormat.Bmp;
- }
- else if (mimeType.EndsWith("png", StringComparison.OrdinalIgnoreCase))
- {
- Format = ImageFormat.Png;
- }
- else
- {
- Format = ImageFormat.Jpg;
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
deleted file mode 100644
index 5df999ab0..000000000
--- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class EpisodeInfo : ItemLookupInfo
- {
- public Dictionary<string, string> SeriesProviderIds { get; set; }
-
- public int? IndexNumberEnd { get; set; }
-
- public bool IsMissingEpisode { get; set; }
-
- public EpisodeInfo()
- {
- SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ExtraInfo.cs b/MediaBrowser.Controller/Providers/ExtraInfo.cs
deleted file mode 100644
index 1fbe6e93a..000000000
--- a/MediaBrowser.Controller/Providers/ExtraInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ExtraInfo
- {
- public string Path { get; set; }
-
- public LocationType LocationType { get; set; }
-
- public bool IsDownloadable { get; set; }
-
- public ExtraType ExtraType { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ExtraSource.cs b/MediaBrowser.Controller/Providers/ExtraSource.cs
deleted file mode 100644
index 901af60f8..000000000
--- a/MediaBrowser.Controller/Providers/ExtraSource.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public enum ExtraSource
- {
- Local = 1,
- Metadata = 2,
- Remote = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/GameInfo.cs b/MediaBrowser.Controller/Providers/GameInfo.cs
deleted file mode 100644
index 771cf6cec..000000000
--- a/MediaBrowser.Controller/Providers/GameInfo.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class GameInfo : ItemLookupInfo
- {
- /// <summary>
- /// Gets or sets the game system.
- /// </summary>
- /// <value>The game system.</value>
- public string GameSystem { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/GameSystemInfo.cs b/MediaBrowser.Controller/Providers/GameSystemInfo.cs
deleted file mode 100644
index efe2635cd..000000000
--- a/MediaBrowser.Controller/Providers/GameSystemInfo.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class GameSystemInfo : ItemLookupInfo
- {
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
deleted file mode 100644
index c9393f4c3..000000000
--- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface ICustomMetadataProvider : IMetadataProvider
- {
- }
-
- public interface ICustomMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ICustomMetadataProvider
- where TItemType : IHasMetadata
- {
- /// <summary>
- /// Fetches the asynchronous.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemUpdateType}.</returns>
- Task<ItemUpdateType> FetchAsync(TItemType item, MetadataRefreshOptions options, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
deleted file mode 100644
index 0b4574f6e..000000000
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IDirectoryService
- {
- FileSystemMetadata[] GetFileSystemEntries(string path);
- List<FileSystemMetadata> GetFiles(string path);
- FileSystemMetadata GetFile(string path);
-
- List<string> GetFilePaths(string path);
- List<string> GetFilePaths(string path, bool clearCache);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
deleted file mode 100644
index e96a2d65e..000000000
--- a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IDynamicImageProvider : IImageProvider
- {
- /// <summary>
- /// Gets the supported images.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IEnumerable{ImageType}.</returns>
- IEnumerable<ImageType> GetSupportedImages(IHasMetadata item);
-
- /// <summary>
- /// Gets the image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="type">The type.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{DynamicImageResponse}.</returns>
- Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IExternalId.cs b/MediaBrowser.Controller/Providers/IExternalId.cs
deleted file mode 100644
index 946f28199..000000000
--- a/MediaBrowser.Controller/Providers/IExternalId.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IExternalId
- {
- string Name { get; }
-
- string Key { get; }
-
- string UrlFormatString { get; }
-
- bool Supports(IHasProviderIds item);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IExtrasProvider.cs b/MediaBrowser.Controller/Providers/IExtrasProvider.cs
deleted file mode 100644
index 3b72232c2..000000000
--- a/MediaBrowser.Controller/Providers/IExtrasProvider.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IExtrasProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Supportses the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool Supports(IHasMetadata item);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IForcedProvider.cs b/MediaBrowser.Controller/Providers/IForcedProvider.cs
deleted file mode 100644
index 9e35b00ad..000000000
--- a/MediaBrowser.Controller/Providers/IForcedProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// This is a marker interface that will cause a provider to run even if IsLocked=true
- /// </summary>
- public interface IForcedProvider
- {
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs
deleted file mode 100644
index 9441c3ecd..000000000
--- a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IHasItemChangeMonitor
- {
- /// <summary>
- /// Determines whether the specified item has changed.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns><c>true</c> if the specified item has changed; otherwise, <c>false</c>.</returns>
- bool HasChanged(IHasMetadata item, IDirectoryService directoryService);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs b/MediaBrowser.Controller/Providers/IHasLookupInfo.cs
deleted file mode 100644
index afce49852..000000000
--- a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public interface IHasLookupInfo<out TLookupInfoType>
- where TLookupInfoType : ItemLookupInfo, new()
- {
- TLookupInfoType GetLookupInfo();
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IHasOrder.cs b/MediaBrowser.Controller/Providers/IHasOrder.cs
deleted file mode 100644
index cb5298dd3..000000000
--- a/MediaBrowser.Controller/Providers/IHasOrder.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public interface IHasOrder
- {
- int Order { get; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs
deleted file mode 100644
index 90f7296c6..000000000
--- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IImageEnhancer
- {
- /// <summary>
- /// Return true only if the given image for the given item will be enhanced by this enhancer.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns><c>true</c> if this enhancer will enhance the supplied image for the supplied item, <c>false</c> otherwise</returns>
- bool Supports(IHasMetadata item, ImageType imageType);
-
- /// <summary>
- /// Gets the priority or order in which this enhancer should be run.
- /// </summary>
- /// <value>The priority.</value>
- MetadataProviderPriority Priority { get; }
-
- /// <summary>
- /// Return a key incorporating all configuration information related to this item
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>Cache key relating to the current state of this item and configuration</returns>
- string GetConfigurationCacheKey(IHasMetadata item, ImageType imageType);
-
- /// <summary>
- /// Gets the size of the enhanced image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <param name="originalImageSize">Size of the original image.</param>
- /// <returns>ImageSize.</returns>
- ImageSize GetEnhancedImageSize(IHasMetadata item, ImageType imageType, int imageIndex, ImageSize originalImageSize);
-
- EnhancedImageInfo GetEnhancedImageInfo(IHasMetadata item, string inputFile, ImageType imageType, int imageIndex);
-
- /// <summary>
- /// Enhances the image async.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="inputFile">The input file.</param>
- /// <param name="outputFile">The output file.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{Image}.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task EnhanceImageAsync(IHasMetadata item, string inputFile, string outputFile, ImageType imageType, int imageIndex);
- }
-
- public class EnhancedImageInfo
- {
- public bool RequiresTransparency { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs
deleted file mode 100644
index 2b43c9cb8..000000000
--- a/MediaBrowser.Controller/Providers/IImageProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Interface IImageProvider
- /// </summary>
- public interface IImageProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Supportses the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- bool Supports(IHasMetadata item);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
deleted file mode 100644
index e93c73a52..000000000
--- a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface ILocalImageFileProvider : ILocalImageProvider
- {
- List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
deleted file mode 100644
index 1027a4cb2..000000000
--- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// This is just a marker interface
- /// </summary>
- public interface ILocalImageProvider : IImageProvider
- {
- }
-}
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
deleted file mode 100644
index 28a535310..000000000
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface ILocalMetadataProvider : IMetadataProvider
- {
- }
-
- public interface ILocalMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ILocalMetadataProvider
- where TItemType : IHasMetadata
- {
- /// <summary>
- /// Gets the metadata.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{MetadataResult{`0}}.</returns>
- Task<MetadataResult<TItemType>> GetMetadata(ItemInfo info,
- IDirectoryService directoryService,
- CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs
deleted file mode 100644
index 0f534c328..000000000
--- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Marker interface
- /// </summary>
- public interface IMetadataProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- }
-
- public interface IMetadataProvider<TItemType> : IMetadataProvider
- where TItemType : IHasMetadata
- {
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs
deleted file mode 100644
index 7b7bf166b..000000000
--- a/MediaBrowser.Controller/Providers/IMetadataService.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IMetadataService
- {
- /// <summary>
- /// Determines whether this instance can refresh the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if this instance can refresh the specified item; otherwise, <c>false</c>.</returns>
- bool CanRefresh(IHasMetadata item);
- bool CanRefreshPrimary(Type type);
-
- /// <summary>
- /// Refreshes the metadata.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="refreshOptions">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the order.
- /// </summary>
- /// <value>The order.</value>
- int Order { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
deleted file mode 100644
index 608674905..000000000
--- a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public interface IPreRefreshProvider : ICustomMetadataProvider
- {
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
deleted file mode 100644
index c2cbda11d..000000000
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Providers;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Interface IProviderManager
- /// </summary>
- public interface IProviderManager
- {
- /// <summary>
- /// Queues the refresh.
- /// </summary>
- void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority);
-
- /// <summary>
- /// Refreshes the full item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RefreshFullItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Refreshes the metadata.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<ItemUpdateType> RefreshSingleItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
-
- /// <summary>
- /// Saves the image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="url">The URL.</param>
- /// <param name="type">The type.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveImage(IHasMetadata item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken);
-
- /// <summary>
- /// Saves the image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="source">The source.</param>
- /// <param name="mimeType">Type of the MIME.</param>
- /// <param name="type">The type.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken);
-
- /// <summary>
- /// Saves the image.
- /// </summary>
- /// <returns>Task.</returns>
- Task SaveImage(IHasMetadata item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
-
- /// <summary>
- /// Adds the metadata providers.
- /// </summary>
- void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders,
- IEnumerable<IMetadataSaver> savers,
- IEnumerable<IExternalId> externalIds);
-
- /// <summary>
- /// Gets the available remote images.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- 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(IHasMetadata item);
-
- /// <summary>
- /// Gets all metadata plugins.
- /// </summary>
- /// <returns>IEnumerable{MetadataPlugin}.</returns>
- MetadataPluginSummary[] GetAllMetadataPlugins();
-
- /// <summary>
- /// Gets the external urls.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IEnumerable{ExternalUrl}.</returns>
- IEnumerable<ExternalUrl> GetExternalUrls(BaseItem item);
-
- /// <summary>
- /// Gets the external identifier infos.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IEnumerable{ExternalIdInfo}.</returns>
- IEnumerable<ExternalIdInfo> GetExternalIdInfos(IHasProviderIds item);
-
- /// <summary>
- /// Saves the metadata.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="updateType">Type of the update.</param>
- /// <returns>Task.</returns>
- void SaveMetadata(IHasMetadata item, ItemUpdateType updateType);
-
- /// <summary>
- /// Saves the metadata.
- /// </summary>
- void SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable<string> savers);
-
- /// <summary>
- /// Gets the metadata options.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>MetadataOptions.</returns>
- MetadataOptions GetMetadataOptions(IHasMetadata item);
-
- /// <summary>
- /// Gets the remote search results.
- /// </summary>
- /// <typeparam name="TItemType">The type of the t item type.</typeparam>
- /// <typeparam name="TLookupType">The type of the t lookup type.</typeparam>
- /// <param name="searchInfo">The search information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{SearchResult{``1}}}.</returns>
- Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(
- RemoteSearchQuery<TLookupType> searchInfo,
- CancellationToken cancellationToken)
- where TItemType : BaseItem, new()
- where TLookupType : ItemLookupInfo;
-
- /// <summary>
- /// Gets the search image.
- /// </summary>
- /// <param name="providerName">Name of the provider.</param>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken);
-
- Dictionary<Guid, Guid> GetRefreshQueue();
-
- void OnRefreshStart(BaseItem item);
- void OnRefreshProgress(BaseItem item, double progress);
- void OnRefreshComplete(BaseItem item);
-
- double? GetRefreshProgress(Guid id);
-
- event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
- event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
- event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
- }
-
- public enum RefreshPriority
- {
- High = 0,
- Normal = 1,
- Low = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
deleted file mode 100644
index 86a7939e6..000000000
--- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Providers;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Interface IImageProvider
- /// </summary>
- public interface IRemoteImageProvider : IImageProvider
- {
- /// <summary>
- /// Gets the supported images.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>IEnumerable{ImageType}.</returns>
- IEnumerable<ImageType> GetSupportedImages(IHasMetadata item);
-
- /// <summary>
- /// Gets the images.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the image response.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
deleted file mode 100644
index 5e2e1b4c7..000000000
--- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Providers;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IRemoteMetadataProvider : IMetadataProvider
- {
- }
-
- public interface IRemoteMetadataProvider<TItemType, in TLookupInfoType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider, IRemoteSearchProvider<TLookupInfoType>
- where TItemType : IHasMetadata, IHasLookupInfo<TLookupInfoType>
- where TLookupInfoType : ItemLookupInfo, new()
- {
- Task<MetadataResult<TItemType>> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken);
- }
-
- public interface IRemoteSearchProvider<in TLookupInfoType> : IRemoteSearchProvider
- where TLookupInfoType : ItemLookupInfo
- {
- Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
deleted file mode 100644
index 0077def42..000000000
--- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IRemoteSearchProvider : IMetadataProvider
- {
- /// <summary>
- /// Gets the image response.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshMode.cs b/MediaBrowser.Controller/Providers/ImageRefreshMode.cs
deleted file mode 100644
index 73ef4b8cd..000000000
--- a/MediaBrowser.Controller/Providers/ImageRefreshMode.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public enum ImageRefreshMode
- {
- /// <summary>
- /// The none
- /// </summary>
- None = 0,
-
- /// <summary>
- /// Existing images will be validated
- /// </summary>
- ValidationOnly = 1,
-
- /// <summary>
- /// The default
- /// </summary>
- Default = 2,
-
- /// <summary>
- /// All providers will be executed to search for new metadata
- /// </summary>
- FullRefresh = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
deleted file mode 100644
index 691ce1ab0..000000000
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ImageRefreshOptions
- {
- public ImageRefreshMode ImageRefreshMode { get; set; }
- public IDirectoryService DirectoryService { get; private set; }
-
- public bool ReplaceAllImages { get; set; }
-
- public List<ImageType> ReplaceImages { get; set; }
- public bool IsAutomated { get; set; }
- public bool ForceEnableInternetMetadata { get; set; }
-
- public ImageRefreshOptions(IDirectoryService directoryService)
- {
- ImageRefreshMode = ImageRefreshMode.Default;
- DirectoryService = directoryService;
-
- ReplaceImages = new List<ImageType>();
- IsAutomated = true;
- }
-
- public bool IsReplacingImage(ImageType type)
- {
- return ImageRefreshMode == ImageRefreshMode.FullRefresh &&
- (ReplaceAllImages || ReplaceImages.Contains(type));
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
deleted file mode 100644
index 63cc48058..000000000
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ItemInfo
- {
- public ItemInfo(IHasMetadata item)
- {
- Path = item.Path;
- ContainingFolderPath = item.ContainingFolderPath;
- IsInMixedFolder = item.IsInMixedFolder;
-
- var video = item as Video;
- if (video != null)
- {
- VideoType = video.VideoType;
- IsPlaceHolder = video.IsPlaceHolder;
- }
-
- ItemType = item.GetType();
- }
-
- public Type ItemType { get; set; }
- public string Path { get; set; }
- public string ContainingFolderPath { get; set; }
- public VideoType VideoType { get; set; }
- public bool IsInMixedFolder { get; set; }
- public bool IsPlaceHolder { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
deleted file mode 100644
index 98122e776..000000000
--- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ItemLookupInfo : IHasProviderIds
- {
- protected static string[] EmptyStringArray = new string[] { };
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the metadata language.
- /// </summary>
- /// <value>The metadata language.</value>
- public string MetadataLanguage { get; set; }
- /// <summary>
- /// Gets or sets the metadata country code.
- /// </summary>
- /// <value>The metadata country code.</value>
- public string MetadataCountryCode { get; set; }
- /// <summary>
- /// Gets or sets the provider ids.
- /// </summary>
- /// <value>The provider ids.</value>
- public Dictionary<string, string> ProviderIds { get; set; }
- /// <summary>
- /// Gets or sets the year.
- /// </summary>
- /// <value>The year.</value>
- public int? Year { get; set; }
- public int? IndexNumber { get; set; }
- public int? ParentIndexNumber { get; set; }
- public DateTime? PremiereDate { get; set; }
- public bool IsAutomated { get; set; }
-
- public ItemLookupInfo()
- {
- IsAutomated = true;
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/LiveTvProgramLookupInfo.cs b/MediaBrowser.Controller/Providers/LiveTvProgramLookupInfo.cs
deleted file mode 100644
index 4e2c11c22..000000000
--- a/MediaBrowser.Controller/Providers/LiveTvProgramLookupInfo.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class LiveTvProgramLookupInfo : ItemLookupInfo
- {
- public Boolean IsMovie { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
deleted file mode 100644
index 5e6efe9f6..000000000
--- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class LocalImageInfo
- {
- public FileSystemMetadata FileInfo { get; set; }
- public ImageType Type { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs b/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs
deleted file mode 100644
index d01261866..000000000
--- a/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Determines when a provider should execute, relative to others
- /// </summary>
- public enum MetadataProviderPriority
- {
- // Run this provider at the beginning
- /// <summary>
- /// The first
- /// </summary>
- First = 1,
-
- // Run this provider after all first priority providers
- /// <summary>
- /// The second
- /// </summary>
- Second = 2,
-
- // Run this provider after all second priority providers
- /// <summary>
- /// The third
- /// </summary>
- Third = 3,
-
- /// <summary>
- /// The fourth
- /// </summary>
- Fourth = 4,
-
- Fifth = 5,
-
- // Run this provider last
- /// <summary>
- /// The last
- /// </summary>
- Last = 999
- }
-}
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs
deleted file mode 100644
index 56492006a..000000000
--- a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public enum MetadataRefreshMode
- {
- /// <summary>
- /// The none
- /// </summary>
- None = 0,
-
- /// <summary>
- /// The validation only
- /// </summary>
- ValidationOnly = 1,
-
- /// <summary>
- /// Providers will be executed based on default rules
- /// </summary>
- Default = 2,
-
- /// <summary>
- /// All providers will be executed to search for new metadata
- /// </summary>
- FullRefresh = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
deleted file mode 100644
index 0df2370bd..000000000
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Providers;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class MetadataRefreshOptions : ImageRefreshOptions
- {
- /// <summary>
- /// When paired with MetadataRefreshMode=FullRefresh, all existing data will be overwritten with new data from the providers.
- /// </summary>
- public bool ReplaceAllMetadata { get; set; }
-
- public bool IsPostRecursiveRefresh { get; set; }
- public bool ValidateChildren { get; set; }
-
- public MetadataRefreshMode MetadataRefreshMode { get; set; }
- public RemoteSearchResult SearchResult { get; set; }
-
- public List<string> RefreshPaths { get; set; }
-
- public bool ForceSave { get; set; }
-
- public MetadataRefreshOptions(IFileSystem fileSystem)
- : this(new DirectoryService(new NullLogger(), fileSystem))
- {
- }
-
- public MetadataRefreshOptions(IDirectoryService directoryService)
- : base(directoryService)
- {
- MetadataRefreshMode = MetadataRefreshMode.Default;
- }
-
- public MetadataRefreshOptions(MetadataRefreshOptions copy)
- : base(copy.DirectoryService)
- {
- MetadataRefreshMode = copy.MetadataRefreshMode;
- ForceSave = copy.ForceSave;
- ReplaceAllMetadata = copy.ReplaceAllMetadata;
-
- ImageRefreshMode = copy.ImageRefreshMode;
- ReplaceAllImages = copy.ReplaceAllImages;
- ReplaceImages = copy.ReplaceImages.ToList();
- SearchResult = copy.SearchResult;
-
- if (copy.RefreshPaths != null && copy.RefreshPaths.Count > 0)
- {
- if (RefreshPaths == null)
- {
- RefreshPaths = new List<string>();
- }
-
- RefreshPaths.AddRange(copy.RefreshPaths);
- }
- }
-
- public bool RefreshItem(BaseItem item)
- {
- if (RefreshPaths != null && RefreshPaths.Count > 0)
- {
- return RefreshPaths.Contains(item.Path ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- }
-
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
deleted file mode 100644
index f35d41ca4..000000000
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class MetadataResult<T>
- {
- public List<LocalImageInfo> Images { get; set; }
- public List<UserItemData> UserDataList { get; set; }
-
- public MetadataResult()
- {
- Images = new List<LocalImageInfo>();
- ResultLanguage = "en";
- }
-
- public List<PersonInfo> People { get; set; }
-
- public bool HasMetadata { get; set; }
- public T Item { get; set; }
- public string ResultLanguage { get; set; }
- public string Provider { get; set; }
- public bool QueriedById { get; set; }
- public void AddPerson(PersonInfo p)
- {
- if (People == null)
- {
- People = new List<PersonInfo>();
- }
-
- PeopleHelper.AddPerson(People, p);
- }
-
- /// <summary>
- /// Not only does this clear, but initializes the list so that services can differentiate between a null list and zero people
- /// </summary>
- public void ResetPeople()
- {
- if (People == null)
- {
- People = new List<PersonInfo>();
- }
- People.Clear();
- }
-
- public UserItemData GetOrAddUserData(string userId)
- {
- if (UserDataList == null)
- {
- UserDataList = new List<UserItemData>();
- }
-
- UserItemData userData = null;
-
- foreach (var i in UserDataList)
- {
- if (string.Equals(userId, i.UserId.ToString("N"), StringComparison.OrdinalIgnoreCase))
- {
- userData = i;
- }
- }
-
- if (userData == null)
- {
- userData = new UserItemData()
- {
- UserId = new Guid(userId)
- };
-
- UserDataList.Add(userData);
- }
-
- return userData;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/MovieInfo.cs b/MediaBrowser.Controller/Providers/MovieInfo.cs
deleted file mode 100644
index 198336fc0..000000000
--- a/MediaBrowser.Controller/Providers/MovieInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class MovieInfo : ItemLookupInfo
- {
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs
deleted file mode 100644
index 4f4ab5954..000000000
--- a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class MusicVideoInfo : ItemLookupInfo
- {
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
deleted file mode 100644
index db4dacb0b..000000000
--- a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class PersonLookupInfo : ItemLookupInfo
- {
-
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
deleted file mode 100644
index cd86f352f..000000000
--- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class RemoteSearchQuery<T>
- where T : ItemLookupInfo
- {
- public T SearchInfo { get; set; }
-
- /// <summary>
- /// If set will only search within the given provider
- /// </summary>
- public string SearchProviderName { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [include disabled providers].
- /// </summary>
- /// <value><c>true</c> if [include disabled providers]; otherwise, <c>false</c>.</value>
- public bool IncludeDisabledProviders { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs
deleted file mode 100644
index 31af268b8..000000000
--- a/MediaBrowser.Controller/Providers/SeasonInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class SeasonInfo : ItemLookupInfo
- {
- public Dictionary<string, string> SeriesProviderIds { get; set; }
-
- public SeasonInfo()
- {
- SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs
deleted file mode 100644
index 0b1361757..000000000
--- a/MediaBrowser.Controller/Providers/SeriesInfo.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class SeriesInfo : ItemLookupInfo
- {
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/SeriesOrderTypes.cs b/MediaBrowser.Controller/Providers/SeriesOrderTypes.cs
deleted file mode 100644
index 5e04fb4db..000000000
--- a/MediaBrowser.Controller/Providers/SeriesOrderTypes.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public static class SeriesOrderTypes
- {
- public const string Anime = "Anime";
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs
deleted file mode 100644
index e3a6f5d37..000000000
--- a/MediaBrowser.Controller/Providers/SongInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Controller.Providers
-{
- public class SongInfo : ItemLookupInfo
- {
- public string[] AlbumArtists { get; set; }
- public string Album { get; set; }
- public string[] Artists { get; set; }
-
- public SongInfo()
- {
- Artists = EmptyStringArray;
- AlbumArtists = EmptyStringArray;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/TrailerInfo.cs b/MediaBrowser.Controller/Providers/TrailerInfo.cs
deleted file mode 100644
index fe26ec43e..000000000
--- a/MediaBrowser.Controller/Providers/TrailerInfo.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public class TrailerInfo : ItemLookupInfo
- {
- public bool IsLocalTrailer { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/VideoContentType.cs b/MediaBrowser.Controller/Providers/VideoContentType.cs
deleted file mode 100644
index 903c77612..000000000
--- a/MediaBrowser.Controller/Providers/VideoContentType.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Enum VideoContentType
- /// </summary>
- public enum VideoContentType
- {
- /// <summary>
- /// The episode
- /// </summary>
- Episode = 0,
-
- /// <summary>
- /// The movie
- /// </summary>
- Movie = 1
- }
-}
diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
deleted file mode 100644
index fc5157d5f..000000000
--- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- /// <summary>
- /// Class ItemResolver
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class ItemResolver<T> : IItemResolver
- where T : BaseItem, new()
- {
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>`0.</returns>
- protected virtual T Resolve(ItemResolveArgs args)
- {
- return null;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public virtual ResolverPriority Priority
- {
- get
- {
- return ResolverPriority.First;
- }
- }
-
- /// <summary>
- /// Sets initial values on the newly resolved item
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected virtual void SetInitialItemValues(T item, ItemResolveArgs args)
- {
- }
-
- /// <summary>
- /// Resolves the path.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BaseItem.</returns>
- BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
- {
- var item = Resolve(args);
-
- if (item != null)
- {
- SetInitialItemValues(item, args);
- }
-
- return item;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
deleted file mode 100644
index 3af5d5f7f..000000000
--- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- /// <summary>
- /// Interface IItemResolver
- /// </summary>
- public interface IItemResolver
- {
- /// <summary>
- /// Resolves the path.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BaseItem.</returns>
- BaseItem ResolvePath(ItemResolveArgs args);
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- ResolverPriority Priority { get; }
- }
-
- public interface IMultiItemResolver
- {
- MultiItemResolverResult ResolveMultiple(Folder parent,
- List<FileSystemMetadata> files,
- string collectionType,
- IDirectoryService directoryService);
- }
-
- public class MultiItemResolverResult
- {
- public List<BaseItem> Items { get; set; }
- public List<FileSystemMetadata> ExtraFiles { get; set; }
-
- public MultiItemResolverResult()
- {
- Items = new List<BaseItem>();
- ExtraFiles = new List<FileSystemMetadata>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
deleted file mode 100644
index 25537193a..000000000
--- a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- /// <summary>
- /// Provides a base "rule" that anyone can use to have paths ignored by the resolver
- /// </summary>
- public interface IResolverIgnoreRule
- {
- bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent);
- }
-}
diff --git a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs
deleted file mode 100644
index df5edeb05..000000000
--- a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Controller.Resolvers
-{
- /// <summary>
- /// Enum ResolverPriority
- /// </summary>
- public enum ResolverPriority
- {
- /// <summary>
- /// The first
- /// </summary>
- First = 1,
- /// <summary>
- /// The second
- /// </summary>
- Second = 2,
- /// <summary>
- /// The third
- /// </summary>
- Third = 3,
- Fourth = 4,
- /// <summary>
- /// The last
- /// </summary>
- Last = 5
- }
-}
diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs
deleted file mode 100644
index c2d4a39d1..000000000
--- a/MediaBrowser.Controller/Security/AuthenticationInfo.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Security
-{
- public class AuthenticationInfo
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- /// <value>The access token.</value>
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the application.
- /// </summary>
- /// <value>The name of the application.</value>
- public string AppName { get; set; }
-
- /// <summary>
- /// Gets or sets the application version.
- /// </summary>
- /// <value>The application version.</value>
- public string AppVersion { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the device.
- /// </summary>
- /// <value>The name of the device.</value>
- public string DeviceName { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is active.
- /// </summary>
- /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value>
- public bool IsActive { get; set; }
-
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- public DateTime DateCreated { get; set; }
-
- /// <summary>
- /// Gets or sets the date revoked.
- /// </summary>
- /// <value>The date revoked.</value>
- public DateTime? DateRevoked { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
deleted file mode 100644
index 273dfd43c..000000000
--- a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-
-namespace MediaBrowser.Controller.Security
-{
- public class AuthenticationInfoQuery
- {
- /// <summary>
- /// Gets or sets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- /// <value>The access token.</value>
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is active.
- /// </summary>
- /// <value><c>null</c> if [is active] contains no value, <c>true</c> if [is active]; otherwise, <c>false</c>.</value>
- public bool? IsActive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has user.
- /// </summary>
- /// <value><c>null</c> if [has user] contains no value, <c>true</c> if [has user]; otherwise, <c>false</c>.</value>
- public bool? HasUser { get; set; }
-
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
deleted file mode 100644
index f80ee6e7f..000000000
--- a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using MediaBrowser.Model.Querying;
-using System.Threading;
-
-namespace MediaBrowser.Controller.Security
-{
- public interface IAuthenticationRepository
- {
- /// <summary>
- /// Creates the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void Create(AuthenticationInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Updates the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- void Update(AuthenticationInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the specified query.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult{AuthenticationInfo}.</returns>
- QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query);
-
- /// <summary>
- /// Gets the specified identifier.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>AuthenticationInfo.</returns>
- AuthenticationInfo Get(string id);
- }
-}
diff --git a/MediaBrowser.Controller/Security/IEncryptionManager.cs b/MediaBrowser.Controller/Security/IEncryptionManager.cs
deleted file mode 100644
index bb4f77d83..000000000
--- a/MediaBrowser.Controller/Security/IEncryptionManager.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-namespace MediaBrowser.Controller.Security
-{
- public interface IEncryptionManager
- {
- /// <summary>
- /// Encrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- string EncryptString(string value);
-
- /// <summary>
- /// Decrypts the string.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>System.String.</returns>
- string DecryptString(string value);
- }
-}
diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs
deleted file mode 100644
index 1b684fa8f..000000000
--- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Controller.Session
-{
- public class AuthenticationRequest
- {
- public string Username { get; set; }
- public string UserId { get; set; }
- public string Password { get; set; }
- public string PasswordSha1 { get; set; }
- public string PasswordMd5 { get; set; }
- public string App { get; set; }
- public string AppVersion { get; set; }
- public string DeviceId { get; set; }
- public string DeviceName { get; set; }
- public string RemoteEndPoint { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs
deleted file mode 100644
index 0d8c207b6..000000000
--- a/MediaBrowser.Controller/Session/ISessionController.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Session
-{
- public interface ISessionController
- {
- /// <summary>
- /// Gets a value indicating whether this instance is session active.
- /// </summary>
- /// <value><c>true</c> if this instance is session active; otherwise, <c>false</c>.</value>
- bool IsSessionActive { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports media remote control].
- /// </summary>
- /// <value><c>true</c> if [supports media remote control]; otherwise, <c>false</c>.</value>
- bool SupportsMediaControl { get; }
-
- /// <summary>
- /// Sends the play command.
- /// </summary>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the playstate command.
- /// </summary>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the generic command.
- /// </summary>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the library update info.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the restart required message.
- /// </summary>
- Task SendRestartRequiredNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the user data change info.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the server shutdown notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendServerShutdownNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the session ended notification.
- /// </summary>
- /// <param name="sessionInfo">The session information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the playback start notification.
- /// </summary>
- /// <param name="sessionInfo">The session information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the playback start notification.
- /// </summary>
- /// <param name="sessionInfo">The session information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the server restart notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendServerRestartNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the message.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="name">The name.</param>
- /// <param name="data">The data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendMessage<T>(string name, T data, CancellationToken cancellationToken);
-
- /// <summary>
- /// Called when [activity].
- /// </summary>
- void OnActivity();
- }
-}
diff --git a/MediaBrowser.Controller/Session/ISessionControllerFactory.cs b/MediaBrowser.Controller/Session/ISessionControllerFactory.cs
deleted file mode 100644
index 92862e462..000000000
--- a/MediaBrowser.Controller/Session/ISessionControllerFactory.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Controller.Session
-{
- /// <summary>
- /// Interface ISesssionControllerFactory
- /// </summary>
- public interface ISessionControllerFactory
- {
- /// <summary>
- /// Gets the session controller.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <returns>ISessionController.</returns>
- ISessionController GetSessionController(SessionInfo session);
- }
-}
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
deleted file mode 100644
index 603e5ef76..000000000
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ /dev/null
@@ -1,336 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Session
-{
- /// <summary>
- /// Interface ISessionManager
- /// </summary>
- public interface ISessionManager
- {
- /// <summary>
- /// Occurs when [playback start].
- /// </summary>
- event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
-
- /// <summary>
- /// Occurs when [playback progress].
- /// </summary>
- event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
-
- /// <summary>
- /// Occurs when [playback stopped].
- /// </summary>
- event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
-
- /// <summary>
- /// Occurs when [session started].
- /// </summary>
- event EventHandler<SessionEventArgs> SessionStarted;
-
- /// <summary>
- /// Occurs when [session ended].
- /// </summary>
- event EventHandler<SessionEventArgs> SessionEnded;
-
- event EventHandler<SessionEventArgs> SessionActivity;
-
- /// <summary>
- /// Occurs when [capabilities changed].
- /// </summary>
- event EventHandler<SessionEventArgs> CapabilitiesChanged;
-
- /// <summary>
- /// Occurs when [authentication failed].
- /// </summary>
- event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
-
- /// <summary>
- /// Occurs when [authentication succeeded].
- /// </summary>
- event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded;
-
- /// <summary>
- /// Gets the sessions.
- /// </summary>
- /// <value>The sessions.</value>
- IEnumerable<SessionInfo> Sessions { get; }
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="sessionFactories">The session factories.</param>
- void AddParts(IEnumerable<ISessionControllerFactory> sessionFactories);
-
- /// <summary>
- /// Logs the user activity.
- /// </summary>
- /// <param name="appName">Type of the client.</param>
- /// <param name="appVersion">The app version.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- Task<SessionInfo> LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user);
-
- /// <summary>
- /// Used to report that playback has started for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- Task OnPlaybackStart(PlaybackStartInfo info);
-
- /// <summary>
- /// Used to report playback progress for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task OnPlaybackProgress(PlaybackProgressInfo info);
-
- Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated);
-
- /// <summary>
- /// Used to report that playback has ended for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task OnPlaybackStopped(PlaybackStopInfo info);
-
- /// <summary>
- /// Reports the session ended.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <returns>Task.</returns>
- void ReportSessionEnded(string sessionId);
-
- /// <summary>
- /// Gets the session info dto.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <returns>SessionInfoDto.</returns>
- SessionInfoDto GetSessionInfoDto(SessionInfo session);
-
- /// <summary>
- /// Sends the general command.
- /// </summary>
- /// <param name="controllingSessionId">The controlling session identifier.</param>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the message command.
- /// </summary>
- /// <param name="controllingSessionId">The controlling session identifier.</param>
- /// <param name="sessionId">The session id.</param>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the play command.
- /// </summary>
- /// <param name="controllingSessionId">The controlling session identifier.</param>
- /// <param name="sessionId">The session id.</param>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the browse command.
- /// </summary>
- /// <param name="controllingSessionId">The controlling session identifier.</param>
- /// <param name="sessionId">The session id.</param>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the playstate command.
- /// </summary>
- /// <param name="controllingSessionId">The controlling session identifier.</param>
- /// <param name="sessionId">The session id.</param>
- /// <param name="command">The command.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the message to admin sessions.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="name">The name.</param>
- /// <param name="data">The data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the message to user sessions.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>Task.</returns>
- Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data, CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the message to user device sessions.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="name">The name.</param>
- /// <param name="data">The data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the restart required message.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendRestartRequiredNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the server shutdown notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendServerShutdownNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Sends the server restart notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SendServerRestartNotification(CancellationToken cancellationToken);
-
- /// <summary>
- /// Adds the additional user.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="userId">The user identifier.</param>
- void AddAdditionalUser(string sessionId, string userId);
-
- /// <summary>
- /// Removes the additional user.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="userId">The user identifier.</param>
- void RemoveAdditionalUser(string sessionId, string userId);
-
- /// <summary>
- /// Reports the now viewing item.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="itemId">The item identifier.</param>
- void ReportNowViewingItem(string sessionId, string itemId);
-
- /// <summary>
- /// Reports the now viewing item.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="item">The item.</param>
- void ReportNowViewingItem(string sessionId, BaseItemDto item);
-
- /// <summary>
- /// Authenticates the new session.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{SessionInfo}.</returns>
- Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request);
-
- /// <summary>
- /// Creates the new session.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;AuthenticationResult&gt;.</returns>
- Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request);
-
- /// <summary>
- /// Reports the capabilities.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="capabilities">The capabilities.</param>
- void ReportCapabilities(string sessionId, ClientCapabilities capabilities);
-
- /// <summary>
- /// Reports the transcoding information.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="info">The information.</param>
- void ReportTranscodingInfo(string deviceId, TranscodingInfo info);
-
- /// <summary>
- /// Clears the transcoding information.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- void ClearTranscodingInfo(string deviceId);
-
- /// <summary>
- /// Gets the session.
- /// </summary>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="client">The client.</param>
- /// <param name="version">The version.</param>
- /// <returns>SessionInfo.</returns>
- SessionInfo GetSession(string deviceId, string client, string version);
-
- /// <summary>
- /// Gets the session by authentication token.
- /// </summary>
- /// <param name="token">The token.</param>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="remoteEndpoint">The remote endpoint.</param>
- /// <returns>SessionInfo.</returns>
- Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint);
-
- /// <summary>
- /// Gets the session by authentication token.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="deviceId">The device identifier.</param>
- /// <param name="remoteEndpoint">The remote endpoint.</param>
- /// <param name="appVersion">The application version.</param>
- /// <returns>Task&lt;SessionInfo&gt;.</returns>
- Task<SessionInfo> GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion);
-
- /// <summary>
- /// Logouts the specified access token.
- /// </summary>
- /// <param name="accessToken">The access token.</param>
- /// <returns>Task.</returns>
- void Logout(string accessToken);
-
- /// <summary>
- /// Revokes the user tokens.
- /// </summary>
- /// <returns>Task.</returns>
- void RevokeUserTokens(string userId, string currentAccessToken);
-
- /// <summary>
- /// Revokes the token.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- void RevokeToken(string id);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Session/SessionEventArgs.cs b/MediaBrowser.Controller/Session/SessionEventArgs.cs
deleted file mode 100644
index 96daa6ec9..000000000
--- a/MediaBrowser.Controller/Session/SessionEventArgs.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Session
-{
- public class SessionEventArgs : EventArgs
- {
- public SessionInfo SessionInfo { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
deleted file mode 100644
index 367a7a467..000000000
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ /dev/null
@@ -1,309 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Threading;
-
-namespace MediaBrowser.Controller.Session
-{
- /// <summary>
- /// Class SessionInfo
- /// </summary>
- public class SessionInfo : IDisposable
- {
- private ISessionManager _sessionManager;
- private readonly ILogger _logger;
-
- public SessionInfo(ISessionManager sessionManager, ILogger logger)
- {
- _sessionManager = sessionManager;
- _logger = logger;
-
- AdditionalUsers = new SessionUserInfo[] { };
- PlayState = new PlayerStateInfo();
- }
-
- public PlayerStateInfo PlayState { get; set; }
-
- public SessionUserInfo[] AdditionalUsers { get; set; }
-
- public ClientCapabilities Capabilities { get; set; }
-
- /// <summary>
- /// Gets or sets the remote end point.
- /// </summary>
- /// <value>The remote end point.</value>
- public string RemoteEndPoint { get; set; }
-
- /// <summary>
- /// Gets or sets the playable media types.
- /// </summary>
- /// <value>The playable media types.</value>
- public string[] PlayableMediaTypes
- {
- get
- {
- if (Capabilities == null)
- {
- return new string[] { };
- }
- return Capabilities.PlayableMediaTypes;
- }
- }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public Guid? UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the username.
- /// </summary>
- /// <value>The username.</value>
- public string UserName { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the client.
- /// </summary>
- /// <value>The type of the client.</value>
- public string Client { get; set; }
-
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime LastActivityDate { get; set; }
-
- /// <summary>
- /// Gets or sets the last playback check in.
- /// </summary>
- /// <value>The last playback check in.</value>
- public DateTime LastPlaybackCheckIn { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the device.
- /// </summary>
- /// <value>The name of the device.</value>
- public string DeviceName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the now viewing item.
- /// </summary>
- /// <value>The name of the now viewing item.</value>
- public BaseItemDto NowViewingItem { get; set; }
-
- /// <summary>
- /// Gets or sets the now playing item.
- /// </summary>
- /// <value>The now playing item.</value>
- public BaseItemDto NowPlayingItem { get; set; }
-
- public BaseItem FullNowPlayingItem { get; set; }
-
- /// <summary>
- /// Gets or sets the device id.
- /// </summary>
- /// <value>The device id.</value>
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Gets or sets the application version.
- /// </summary>
- /// <value>The application version.</value>
- public string ApplicationVersion { get; set; }
-
- /// <summary>
- /// Gets or sets the session controller.
- /// </summary>
- /// <value>The session controller.</value>
- public ISessionController SessionController { get; set; }
-
- /// <summary>
- /// Gets or sets the application icon URL.
- /// </summary>
- /// <value>The application icon URL.</value>
- public string AppIconUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the supported commands.
- /// </summary>
- /// <value>The supported commands.</value>
- public string[] SupportedCommands
- {
- get
- {
- if (Capabilities == null)
- {
- return new string[] { };
- }
- return Capabilities.SupportedCommands;
- }
- }
-
- public TranscodingInfo TranscodingInfo { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is active.
- /// </summary>
- /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value>
- public bool IsActive
- {
- get
- {
- if (SessionController != null)
- {
- return SessionController.IsSessionActive;
- }
-
- return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 10;
- }
- }
-
- public bool SupportsMediaControl
- {
- get
- {
- if (Capabilities == null || !Capabilities.SupportsMediaControl)
- {
- return false;
- }
-
- if (SessionController != null)
- {
- return SessionController.SupportsMediaControl;
- }
-
- return false;
- }
- }
-
- public bool ContainsUser(string userId)
- {
- return ContainsUser(new Guid(userId));
- }
-
- public bool ContainsUser(Guid userId)
- {
- if ((UserId ?? Guid.Empty) == userId)
- {
- return true;
- }
-
- foreach (var additionalUser in AdditionalUsers)
- {
- if (userId == new Guid(additionalUser.UserId))
- {
- return true;
- }
- }
- return false;
- }
-
- private readonly object _progressLock = new object();
- private ITimer _progressTimer;
- private PlaybackProgressInfo _lastProgressInfo;
-
- public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo)
- {
- if (_disposed)
- {
- return;
- }
-
- lock (_progressLock)
- {
- _lastProgressInfo = progressInfo;
-
- if (_progressTimer == null)
- {
- _progressTimer = timerFactory.Create(OnProgressTimerCallback, null, 1000, 1000);
- }
- else
- {
- _progressTimer.Change(1000, 1000);
- }
- }
- }
-
- // 1 second
- private const long ProgressIncrement = 10000000;
-
- private async void OnProgressTimerCallback(object state)
- {
- if (_disposed)
- {
- return;
- }
-
- var progressInfo = _lastProgressInfo;
- if (progressInfo == null)
- {
- return;
- }
- if (progressInfo.IsPaused)
- {
- return;
- }
-
- var positionTicks = progressInfo.PositionTicks ?? 0;
- if (positionTicks < 0)
- {
- positionTicks = 0;
- }
-
- var newPositionTicks = positionTicks + ProgressIncrement;
- var item = progressInfo.Item;
- long? runtimeTicks = item == null ? null : item.RunTimeTicks;
-
- // Don't report beyond the runtime
- if (runtimeTicks.HasValue && newPositionTicks >= runtimeTicks.Value)
- {
- return;
- }
-
- progressInfo.PositionTicks = newPositionTicks;
-
- try
- {
- await _sessionManager.OnPlaybackProgress(progressInfo, true).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reporting playback progress", ex);
- }
- }
-
- public void StopAutomaticProgress()
- {
- lock (_progressLock)
- {
- if (_progressTimer != null)
- {
- _progressTimer.Dispose();
- _progressTimer = null;
- }
- _lastProgressInfo = null;
- }
- }
-
- private bool _disposed = false;
-
- public void Dispose()
- {
- _disposed = true;
-
- StopAutomaticProgress();
- _sessionManager = null;
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs
deleted file mode 100644
index 6d0b95bcb..000000000
--- a/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Sorting
-{
- /// <summary>
- /// Interface IBaseItemComparer
- /// </summary>
- public interface IBaseItemComparer : IComparer<BaseItem>
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
deleted file mode 100644
index 915d4854b..000000000
--- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Controller.Sorting
-{
- /// <summary>
- /// Represents a BaseItem comparer that requires a User to perform it's comparison
- /// </summary>
- public interface IUserBaseItemComparer : IBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- User User { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- IUserDataManager UserDataRepository { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs
deleted file mode 100644
index ec8ee5a11..000000000
--- a/MediaBrowser.Controller/Sorting/SortExtensions.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace MediaBrowser.Controller.Sorting
-{
- public static class SortExtensions
- {
- public static IEnumerable<T> OrderByString<T>(this IEnumerable<T> list, Func<T, string> getName)
- {
- return list.OrderBy(getName, new AlphanumComparator());
- }
-
- public static IEnumerable<T> OrderByStringDescending<T>(this IEnumerable<T> list, Func<T, string> getName)
- {
- return list.OrderByDescending(getName, new AlphanumComparator());
- }
-
- public static IOrderedEnumerable<T> ThenByString<T>(this IOrderedEnumerable<T> list, Func<T, string> getName)
- {
- return list.ThenBy(getName, new AlphanumComparator());
- }
-
- public static IOrderedEnumerable<T> ThenByStringDescending<T>(this IOrderedEnumerable<T> list, Func<T, string> getName)
- {
- return list.ThenByDescending(getName, new AlphanumComparator());
- }
-
- private class AlphanumComparator : IComparer<string>
- {
- private enum ChunkType { Alphanumeric, Numeric };
-
- private static bool InChunk(char ch, char otherCh)
- {
- var type = ChunkType.Alphanumeric;
-
- if (char.IsDigit(otherCh))
- {
- type = ChunkType.Numeric;
- }
-
- if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
- || (type == ChunkType.Numeric && !char.IsDigit(ch)))
- {
- return false;
- }
-
- return true;
- }
-
- public static int CompareValues(string s1, string s2)
- {
- if (s1 == null || s2 == null)
- {
- return 0;
- }
-
- int thisMarker = 0, thisNumericChunk = 0;
- int thatMarker = 0, thatNumericChunk = 0;
-
- while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
- {
- if (thisMarker >= s1.Length)
- {
- return -1;
- }
- else if (thatMarker >= s2.Length)
- {
- return 1;
- }
- char thisCh = s1[thisMarker];
- char thatCh = s2[thatMarker];
-
- StringBuilder thisChunk = new StringBuilder();
- StringBuilder thatChunk = new StringBuilder();
-
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0])))
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0])))
- {
- thatChunk.Append(thatCh);
- thatMarker++;
-
- if (thatMarker < s2.Length)
- {
- thatCh = s2[thatMarker];
- }
- }
-
- int result = 0;
- // If both chunks contain numeric characters, sort them numerically
- if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
- {
- if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk))
- {
- return 0;
- }
- if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk))
- {
- return 0;
- }
-
- if (thisNumericChunk < thatNumericChunk)
- {
- result = -1;
- }
-
- if (thisNumericChunk > thatNumericChunk)
- {
- result = 1;
- }
- }
- else
- {
- result = thisChunk.ToString().CompareTo(thatChunk.ToString());
- }
-
- if (result != 0)
- {
- return result;
- }
- }
-
- return 0;
- }
-
- public int Compare(string x, string y)
- {
- return CompareValues(x, y);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Sorting/SortHelper.cs b/MediaBrowser.Controller/Sorting/SortHelper.cs
deleted file mode 100644
index 3456b9b04..000000000
--- a/MediaBrowser.Controller/Sorting/SortHelper.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Controller.Sorting
-{
- public static class SortHelper
- {
- private enum ChunkType { Alphanumeric, Numeric };
-
- public static bool InChunk(char ch, char otherCh)
- {
- var type = ChunkType.Alphanumeric;
-
- if (char.IsDigit(otherCh))
- {
- type = ChunkType.Numeric;
- }
-
- if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
- || (type == ChunkType.Numeric && !char.IsDigit(ch)))
- {
- return false;
- }
-
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
deleted file mode 100644
index 2199c21e6..000000000
--- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Providers;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public interface ISubtitleManager
- {
- /// <summary>
- /// Occurs when [subtitle download failure].
- /// </summary>
- event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
-
- /// <summary>
- /// Occurs when [subtitles downloaded].
- /// </summary>
- event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="subtitleProviders">The subtitle providers.</param>
- void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders);
-
- /// <summary>
- /// Searches the subtitles.
- /// </summary>
- Task<RemoteSubtitleInfo[]> SearchSubtitles(Video video,
- string language,
- bool? isPerfectMatch,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Searches the subtitles.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns>
- Task<RemoteSubtitleInfo[]> SearchSubtitles(SubtitleSearchRequest request,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Downloads the subtitles.
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="subtitleId">The subtitle identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task DownloadSubtitles(Video video,
- string subtitleId,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the remote subtitles.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SubtitleResponse}.</returns>
- Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the subtitles.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="index">The index.</param>
- /// <returns>Task.</returns>
- Task DeleteSubtitles(string itemId, int index);
-
- /// <summary>
- /// Gets the providers.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <returns>IEnumerable{SubtitleProviderInfo}.</returns>
- SubtitleProviderInfo[] GetProviders(string itemId);
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
deleted file mode 100644
index 5cb106fec..000000000
--- a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Providers;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public interface ISubtitleProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the supported media types.
- /// </summary>
- /// <value>The supported media types.</value>
- IEnumerable<VideoContentType> SupportedMediaTypes { get; }
-
- /// <summary>
- /// Searches the subtitles.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns>
- Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the subtitles.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{SubtitleResponse}.</returns>
- Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the supported languages.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;IEnumerable&lt;NameIdPair&gt;&gt;.</returns>
- Task<IEnumerable<NameIdPair>> GetSupportedLanguages(CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
deleted file mode 100644
index 1d204f2cb..000000000
--- a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public class SubtitleDownloadEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Format { get; set; }
-
- public string Language { get; set; }
-
- public bool IsForced { get; set; }
-
- public string Provider { get; set; }
- }
-
- public class SubtitleDownloadFailureEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Provider { get; set; }
-
- public Exception Exception { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
deleted file mode 100644
index e2f6dfc97..000000000
--- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.IO;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public class SubtitleResponse
- {
- public string Language { get; set; }
- public string Format { get; set; }
- public bool IsForced { get; set; }
- public Stream Stream { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
deleted file mode 100644
index a2371426a..000000000
--- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public class SubtitleSearchRequest : IHasProviderIds
- {
- public string Language { get; set; }
-
- public VideoContentType ContentType { get; set; }
-
- public string MediaPath { get; set; }
- public string SeriesName { get; set; }
- public string Name { get; set; }
- public int? IndexNumber { get; set; }
- public int? IndexNumberEnd { get; set; }
- public int? ParentIndexNumber { get; set; }
- public int? ProductionYear { get; set; }
- public long? RuntimeTicks { get; set; }
- public bool IsPerfectMatch { get; set; }
- public Dictionary<string, string> ProviderIds { get; set; }
-
- public bool SearchAllProviders { get; set; }
-
- public SubtitleSearchRequest()
- {
- SearchAllProviders = true;
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
deleted file mode 100644
index cf868a381..000000000
--- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using MediaBrowser.Model.Sync;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface IHasDynamicAccess
- {
- /// <summary>
- /// Gets the synced file information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="target">The target.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;SyncedFileInfo&gt;.</returns>
- Task<SyncedFileInfo> GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs
deleted file mode 100644
index aeb7a3bff..000000000
--- a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Controller.Sync
-{
- /// <summary>
- /// A marker interface
- /// </summary>
- public interface IRemoteSyncProvider
- {
- }
-}
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
deleted file mode 100644
index 3913e86f0..000000000
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface IServerSyncProvider : ISyncProvider
- {
- /// <summary>
- /// Transfers the file.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <param name="pathParts">The path parts.</param>
- /// <param name="target">The target.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<SyncedFileInfo> SendFile(Stream stream, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
-
- /// <summary>
- /// Deletes the file.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="target">The target.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task DeleteFile(string id, SyncTarget target, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the file.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="target">The target.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;Stream&gt;.</returns>
- Task<Stream> GetFile(string id, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
-
- Task<QueryResult<FileSystemMetadata>> GetFiles(string[] pathParts, SyncTarget target, CancellationToken cancellationToken);
- Task<QueryResult<FileSystemMetadata>> GetFiles(SyncTarget target, CancellationToken cancellationToken);
- }
-
- public interface ISupportsDirectCopy
- {
- /// <summary>
- /// Sends the file.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="pathParts">The path parts.</param>
- /// <param name="target">The target.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;SyncedFileInfo&gt;.</returns>
- Task<SyncedFileInfo> SendFile(string path, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
- }
-
- public interface IHasDuplicateCheck
- {
- /// <summary>
- /// Allows the duplicate job item.
- /// </summary>
- /// <param name="original">The original.</param>
- /// <param name="duplicate">The duplicate.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs
deleted file mode 100644
index ebff50cb0..000000000
--- a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface ISyncDataProvider
- {
- /// <summary>
- /// Gets the local items.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="serverId">The server identifier.</param>
- /// <returns>Task&lt;List&lt;LocalItem&gt;&gt;.</returns>
- Task<List<LocalItem>> GetLocalItems(SyncTarget target, string serverId);
-
- /// <summary>
- /// Adds the or update.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- Task AddOrUpdate(SyncTarget target, LocalItem item);
-
- /// <summary>
- /// Deletes the specified identifier.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task Delete(SyncTarget target, string id);
-
- /// <summary>
- /// Gets the specified identifier.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="id">The identifier.</param>
- /// <returns>Task&lt;LocalItem&gt;.</returns>
- Task<LocalItem> Get(SyncTarget target, string id);
-
- /// <summary>
- /// Gets the cached item.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="serverId">The server identifier.</param>
- /// <param name="itemId">The item identifier.</param>
- /// <returns>Task&lt;LocalItem&gt;.</returns>
- Task<List<LocalItem>> GetItems(SyncTarget target, string serverId, string itemId);
- /// <summary>
- /// Gets the cached items by synchronize job item identifier.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="serverId">The server identifier.</param>
- /// <param name="syncJobItemId">The synchronize job item identifier.</param>
- /// <returns>Task&lt;List&lt;LocalItem&gt;&gt;.</returns>
- Task<List<LocalItem>> GetItemsBySyncJobItemId(SyncTarget target, string serverId, string syncJobItemId);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
deleted file mode 100644
index 5e9085a40..000000000
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface ISyncManager
- {
- event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
- event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
- event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
- event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
- event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
- event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCancelled;
-
- /// <summary>
- /// Creates the job.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- Task<SyncJobCreationResult> CreateJob(SyncJobRequest request);
-
- /// <summary>
- /// Gets the jobs.
- /// </summary>
- /// <returns>QueryResult&lt;SyncJob&gt;.</returns>
- QueryResult<SyncJob> GetJobs(SyncJobQuery query);
-
- /// <summary>
- /// Gets the job items.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;SyncJobItem&gt;.</returns>
- QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
-
- /// <summary>
- /// Gets the job.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SyncJob.</returns>
- SyncJob GetJob(string id);
-
- /// <summary>
- /// Updates the job.
- /// </summary>
- /// <param name="job">The job.</param>
- /// <returns>Task.</returns>
- Task UpdateJob(SyncJob job);
-
- /// <summary>
- /// Res the enable job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task ReEnableJobItem(string id);
-
- /// <summary>
- /// Cnacels the job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelJobItem(string id);
-
- /// <summary>
- /// Cancels the job.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelJob(string id);
-
- /// <summary>
- /// Cancels the items.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- /// <param name="itemIds">The item ids.</param>
- /// <returns>Task.</returns>
- Task CancelItems(string targetId, string[] itemIds);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- void AddParts(IEnumerable<ISyncProvider> providers);
-
- /// <summary>
- /// Gets the synchronize targets.
- /// </summary>
- List<SyncTarget> GetSyncTargets(string userId);
-
- /// <summary>
- /// Supportses the synchronize.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool SupportsSync(BaseItem item);
-
- /// <summary>
- /// Reports the synchronize job item transferred.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task ReportSyncJobItemTransferred(string id);
-
- /// <summary>
- /// Gets the job item.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SyncJobItem.</returns>
- SyncJobItem GetJobItem(string id);
-
- /// <summary>
- /// Reports the offline action.
- /// </summary>
- /// <param name="action">The action.</param>
- /// <returns>Task.</returns>
- Task ReportOfflineAction(UserAction action);
-
- /// <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 library item ids.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult&lt;System.String&gt;.</returns>
- Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query);
-
- /// <summary>
- /// Reports the synchronize job item transfer beginning.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task ReportSyncJobItemTransferBeginning(string id);
-
- /// <summary>
- /// Reports the synchronize job item transfer failed.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task ReportSyncJobItemTransferFailed(string id);
-
- /// <summary>
- /// Gets the quality options.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- List<SyncQualityOption> GetQualityOptions(string targetId);
- /// <summary>
- /// Gets the quality options.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- /// <param name="user">The user.</param>
- List<SyncQualityOption> GetQualityOptions(string targetId, User user);
-
- /// <summary>
- /// Gets the profile options.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- List<SyncProfileOption> GetProfileOptions(string targetId);
- /// <summary>
- /// Gets the profile options.
- /// </summary>
- /// <param name="targetId">The target identifier.</param>
- /// <param name="user">The user.</param>
- List<SyncProfileOption> GetProfileOptions(string targetId, User user);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
deleted file mode 100644
index baee1a52f..000000000
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface ISyncProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the synchronize targets.
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>IEnumerable&lt;SyncTarget&gt;.</returns>
- List<SyncTarget> GetSyncTargets(string userId);
-
- /// <summary>
- /// Gets all synchronize targets.
- /// </summary>
- /// <returns>IEnumerable&lt;SyncTarget&gt;.</returns>
- List<SyncTarget> GetAllSyncTargets();
- }
-
- public interface IHasUniqueTargetIds
- {
-
- }
-}
diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
deleted file mode 100644
index 844e7d890..000000000
--- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Sync
-{
- public class SyncedFileInfo
- {
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
- /// <summary>
- /// Gets or sets the protocol.
- /// </summary>
- /// <value>The protocol.</value>
- public MediaProtocol Protocol { get; set; }
- /// <summary>
- /// Gets or sets the required HTTP headers.
- /// </summary>
- /// <value>The required HTTP headers.</value>
- public Dictionary<string, string> RequiredHttpHeaders { get; set; }
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- public SyncedFileInfo()
- {
- RequiredHttpHeaders = new Dictionary<string, string>();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Sync/SyncedItemProgress.cs b/MediaBrowser.Controller/Sync/SyncedItemProgress.cs
deleted file mode 100644
index 0fd929eb1..000000000
--- a/MediaBrowser.Controller/Sync/SyncedItemProgress.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Controller.Sync
-{
- public class SyncedItemProgress
- {
- public double Progress { get; set; }
- public SyncJobItemStatus Status { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/TV/ITVSeriesManager.cs b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
deleted file mode 100644
index fd41094ee..000000000
--- a/MediaBrowser.Controller/TV/ITVSeriesManager.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Querying;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Dto;
-
-namespace MediaBrowser.Controller.TV
-{
- public interface ITVSeriesManager
- {
- /// <summary>
- /// Gets the next up.
- /// </summary>
- QueryResult<BaseItem> GetNextUp(NextUpQuery query, DtoOptions options);
-
- /// <summary>
- /// Gets the next up.
- /// </summary>
- QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<BaseItem> parentsFolders, DtoOptions options);
- }
-}
diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
index 9400cccf3..ecd255c18 100644
--- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
@@ -10,11 +10,11 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata
{
public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasItemChangeMonitor, IHasOrder
- where T : IHasMetadata, new()
+ where T : BaseItem, new()
{
protected IFileSystem FileSystem;
- public async Task<MetadataResult<T>> GetMetadata(ItemInfo info,
+ public Task<MetadataResult<T>> GetMetadata(ItemInfo info,
IDirectoryService directoryService,
CancellationToken cancellationToken)
{
@@ -24,7 +24,7 @@ namespace MediaBrowser.LocalMetadata
if (file == null)
{
- return result;
+ return Task.FromResult(result);
}
var path = file.FullName;
@@ -45,7 +45,7 @@ namespace MediaBrowser.LocalMetadata
result.HasMetadata = false;
}
- return result;
+ return Task.FromResult(result);
}
protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken);
@@ -57,7 +57,7 @@ namespace MediaBrowser.LocalMetadata
protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
var file = GetXmlFile(new ItemInfo(item), directoryService);
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 4ec2eeeb1..de14cec38 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is CollectionFolder && item.SupportsLocalMetadata;
}
@@ -35,7 +35,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService)
{
var collectionFolder = (CollectionFolder)item;
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index 9195a9cd4..eaf3343e3 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -29,12 +29,12 @@ namespace MediaBrowser.LocalMetadata.Images
get { return 0; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Episode && item.SupportsLocalMetadata;
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService)
{
var parentPath = _fileSystem.GetDirectoryName(item.Path);
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 469a31442..cc06e04e8 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
if (item is Photo)
{
@@ -61,7 +61,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService)
{
var path = item.GetInternalMetadataPath();
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 2901f51d5..1fbdcf39a 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -32,12 +32,12 @@ namespace MediaBrowser.LocalMetadata.Images
get { return 0; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
if (item.SupportsLocalMetadata)
{
// Episode has it's own provider
- if (item.IsOwnedItem || item is Episode || item is Audio || item is Photo)
+ if (item is Episode || item is Audio || item is Photo)
{
return false;
}
@@ -53,7 +53,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
var series = season.Series;
- if (series != null && series.LocationType == LocationType.FileSystem)
+ if (series != null && series.IsFileProtocol)
{
return true;
}
@@ -63,9 +63,9 @@ namespace MediaBrowser.LocalMetadata.Images
return false;
}
- private IEnumerable<FileSystemMetadata> GetFiles(IHasMetadata item, bool includeDirectories, IDirectoryService directoryService)
+ private IEnumerable<FileSystemMetadata> GetFiles(BaseItem item, bool includeDirectories, IDirectoryService directoryService)
{
- if (item.LocationType != LocationType.FileSystem)
+ if (!item.IsFileProtocol)
{
return new List<FileSystemMetadata>();
}
@@ -85,7 +85,7 @@ namespace MediaBrowser.LocalMetadata.Images
.OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty));
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService)
{
var files = GetFiles(item, true, directoryService).ToList();
@@ -96,12 +96,12 @@ namespace MediaBrowser.LocalMetadata.Images
return list;
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, string path, bool isPathInMediaFolder, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, string path, bool isPathInMediaFolder, IDirectoryService directoryService)
{
return GetImages(item, new[] { path }, isPathInMediaFolder, directoryService);
}
- public List<LocalImageInfo> GetImages(IHasMetadata item, IEnumerable<string> paths, bool arePathsInMediaFolders, IDirectoryService directoryService)
+ public List<LocalImageInfo> GetImages(BaseItem item, IEnumerable<string> paths, bool arePathsInMediaFolders, IDirectoryService directoryService)
{
IEnumerable<FileSystemMetadata> files = paths.SelectMany(i => _fileSystem.GetFiles(i, BaseItem.SupportedImageExtensions, true, false));
@@ -115,7 +115,7 @@ namespace MediaBrowser.LocalMetadata.Images
return list;
}
- private void PopulateImages(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
+ private void PopulateImages(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
{
if (supportParentSeriesFiles)
{
@@ -259,7 +259,7 @@ namespace MediaBrowser.LocalMetadata.Images
"movie"
};
- private void PopulatePrimaryImages(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder)
+ private void PopulatePrimaryImages(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder)
{
string[] imageFileNames;
@@ -315,7 +315,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
- private void PopulateBackdrops(IHasMetadata item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
+ private void PopulateBackdrops(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
{
if (!string.IsNullOrEmpty(item.Path))
{
@@ -416,7 +416,7 @@ namespace MediaBrowser.LocalMetadata.Images
var seasonNumber = season.IndexNumber;
var series = season.Series;
- if (!seasonNumber.HasValue || series.LocationType != LocationType.FileSystem)
+ if (!seasonNumber.HasValue || !series.IsFileProtocol)
{
return;
}
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index 0986ffdc2..afd3925ba 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -1,85 +1,17 @@
-<?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>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.LocalMetadata</RootNamespace>
- <AssemblyName>MediaBrowser.LocalMetadata</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <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>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="BaseXmlProvider.cs" />
- <Compile Include="Images\CollectionFolderImageProvider.cs" />
- <Compile Include="Images\EpisodeLocalImageProvider.cs" />
- <Compile Include="Images\InternalMetadataFolderImageProvider.cs" />
- <Compile Include="Images\LocalImageProvider.cs" />
- <Compile Include="Parsers\BaseItemXmlParser.cs" />
- <Compile Include="Parsers\BoxSetXmlParser.cs" />
- <Compile Include="Parsers\GameSystemXmlParser.cs" />
- <Compile Include="Parsers\GameXmlParser.cs" />
- <Compile Include="Parsers\PlaylistXmlParser.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Providers\BoxSetXmlProvider.cs" />
- <Compile Include="Providers\FolderXmlProvider.cs" />
- <Compile Include="Providers\GameSystemXmlProvider.cs" />
- <Compile Include="Providers\GameXmlProvider.cs" />
- <Compile Include="Providers\PlaylistXmlProvider.cs" />
- <Compile Include="Savers\BaseXmlSaver.cs" />
- <Compile Include="Savers\BoxSetXmlSaver.cs" />
- <Compile Include="Savers\FolderXmlSaver.cs" />
- <Compile Include="Savers\GameSystemXmlSaver.cs" />
- <Compile Include="Savers\GameXmlSaver.cs" />
- <Compile Include="Savers\PersonXmlSaver.cs" />
- <Compile Include="Savers\PlaylistXmlSaver.cs" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.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.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 9a814213b..05874ec2c 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -253,18 +253,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
- case "Website":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.HomePageUrl = val;
- }
-
- break;
- }
-
case "LockedFields":
{
var val = reader.ReadElementContentAsString();
@@ -462,13 +450,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
var val = reader.ReadElementContentAsString();
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
+ if (!string.IsNullOrWhiteSpace(val))
{
- if (!string.IsNullOrWhiteSpace(val))
- {
- hasTrailers.AddTrailerUrl(val);
- }
+ item.AddTrailerUrl(val);
}
break;
}
@@ -494,11 +478,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
using (var subtree = reader.ReadSubtree())
{
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- FetchDataFromTrailersNode(subtree, hasTrailers);
- }
+ FetchDataFromTrailersNode(subtree, item);
}
}
else
@@ -726,9 +706,10 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
}
}
-
private void FetchFromSharesNode(XmlReader reader, IHasShares item)
{
+ var list = new List<Share>();
+
reader.MoveToContent();
reader.Read();
@@ -746,20 +727,24 @@ namespace MediaBrowser.LocalMetadata.Parsers
reader.Read();
continue;
}
- using (var subtree = reader.ReadSubtree())
+
+ using (var subReader = reader.ReadSubtree())
{
- var share = GetShareFromNode(subtree);
- if (share != null)
+ var child = GetShare(subReader);
+
+ if (child != null)
{
- item.Shares.Add(share);
+ list.Add(child);
}
}
+
break;
}
-
default:
- reader.Skip();
- break;
+ {
+ reader.Skip();
+ break;
+ }
}
}
else
@@ -767,6 +752,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
reader.Read();
}
}
+
+ item.Shares = list.ToArray();
}
private Share GetShareFromNode(XmlReader reader)
@@ -1012,7 +999,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
}
- private void FetchDataFromTrailersNode(XmlReader reader, IHasTrailers item)
+ private void FetchDataFromTrailersNode(XmlReader reader, T item)
{
reader.MoveToContent();
reader.Read();
@@ -1193,6 +1180,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
linkedItem.Path = reader.ReadElementContentAsString();
break;
}
+ case "ItemId":
+ {
+ linkedItem.LibraryItemId = reader.ReadElementContentAsString();
+ break;
+ }
default:
reader.Skip();
@@ -1206,7 +1198,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
// This is valid
- if (!string.IsNullOrWhiteSpace(linkedItem.Path))
+ if (!string.IsNullOrWhiteSpace(linkedItem.Path) || !string.IsNullOrWhiteSpace(linkedItem.LibraryItemId))
{
return linkedItem;
}
@@ -1277,7 +1269,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
value = value.Trim().Trim(separator);
- return string.IsNullOrWhiteSpace(value) ? new string[] { } : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
+ return string.IsNullOrWhiteSpace(value) ? Array.Empty<string>() : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
@@ -1287,7 +1279,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
/// <param name="separators">The separators.</param>
/// <param name="options">The options.</param>
/// <returns>System.String[][].</returns>
- private static string[] Split(string val, char[] separators, StringSplitOptions options)
+ private string[] Split(string val, char[] separators, StringSplitOptions options)
{
return val.Split(separators, options);
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
index 8bf09e546..3225603bf 100644
--- a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
@@ -12,14 +12,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
public class GameSystemXmlParser : BaseItemXmlParser<GameSystem>
{
- private readonly Task _cachedTask = Task.FromResult(true);
public Task FetchAsync(MetadataResult<GameSystem> item, string metadataFile, CancellationToken cancellationToken)
{
Fetch(item, metadataFile, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
- return _cachedTask;
+ return Task.CompletedTask;
}
/// <summary>
diff --git a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
index abce0582c..f9b334396 100644
--- a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
@@ -18,14 +18,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly Task _cachedTask = Task.FromResult(true);
public Task FetchAsync(MetadataResult<Game> item, string metadataFile, CancellationToken cancellationToken)
{
Fetch(item, metadataFile, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
- return _cachedTask;
+ return Task.CompletedTask;
}
/// <summary>
diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
index c6ef85814..10f46e5a4 100644
--- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
@@ -20,21 +20,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
switch (reader.Name)
{
- case "OwnerUserId":
- {
- var userId = reader.ReadElementContentAsString();
- if (!item.Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)))
- {
- item.Shares.Add(new Share
- {
- UserId = userId,
- CanEdit = true
- });
- }
-
- break;
- }
-
case "PlaylistMediaType":
{
item.PlaylistMediaType = reader.ReadElementContentAsString();
@@ -57,21 +42,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
break;
- case "Shares":
-
- if (!reader.IsEmptyElement)
- {
- using (var subReader = reader.ReadSubtree())
- {
- FetchFromSharesNode(subReader, item);
- }
- }
- else
- {
- reader.Read();
- }
- break;
-
default:
base.FetchDataFromXmlNode(reader, result);
break;
@@ -128,56 +98,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
item.LinkedChildren = list.ToArray(list.Count);
}
- private void FetchFromSharesNode(XmlReader reader, Playlist item)
- {
- var list = new List<Share>();
-
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "Share":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
-
- using (var subReader = reader.ReadSubtree())
- {
- var child = GetShare(subReader);
-
- if (child != null)
- {
- list.Add(child);
- }
- }
-
- break;
- }
- default:
- {
- reader.Skip();
- break;
- }
- }
- }
- else
- {
- reader.Read();
- }
- }
-
- item.Shares = list;
- }
-
public PlaylistXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
{
}
diff --git a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
deleted file mode 100644
index 0b596a5be..000000000
--- a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System.IO;
-using System.Threading;
-
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.LocalMetadata.Parsers;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Xml;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
- /// <summary>
- /// Provides metadata for Folders and all subclasses by parsing folder.xml
- /// </summary>
- public class FolderXmlProvider : BaseXmlProvider<Folder>
- {
- private readonly ILogger _logger;
- private readonly IProviderManager _providerManager;
- protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
-
- public FolderXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
- : base(fileSystem)
- {
- _logger = logger;
- _providerManager = providerManager;
- XmlReaderSettingsFactory = xmlReaderSettingsFactory;
- }
-
- protected override void Fetch(MetadataResult<Folder> result, string path, CancellationToken cancellationToken)
- {
- new BaseItemXmlParser<Folder>(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
- }
-
- protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
- {
- return directoryService.GetFile(Path.Combine(info.Path, "folder.xml"));
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index 4d0495acb..909daf2dd 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -4,7 +4,7 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-
+using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Xml;
@@ -32,7 +32,7 @@ namespace MediaBrowser.LocalMetadata.Providers
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
- return directoryService.GetFile(Path.Combine(info.Path, "playlist.xml"));
+ return directoryService.GetFile(PlaylistXmlSaver.GetSavePath(info.Path, FileSystem));
}
}
}
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index a3800e5c1..81622f54c 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -29,92 +29,6 @@ namespace MediaBrowser.LocalMetadata.Savers
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- private static readonly Dictionary<string, string> CommonTags = new[] {
-
- "Added",
- "AspectRatio",
- "AudioDbAlbumId",
- "AudioDbArtistId",
- "BirthDate",
-
- // Deprecated. No longer saving in this field.
- "certification",
-
- "Chapters",
- "ContentRating",
- "Countries",
- "CustomRating",
- "CriticRating",
- "DeathDate",
- "DisplayOrder",
- "EndDate",
- "Genres",
- "Genre",
- "GamesDbId",
-
- // Deprecated. No longer saving in this field.
- "IMDB_ID",
-
- "IMDB",
-
- // Deprecated. No longer saving in this field.
- "IMDbId",
-
- "Language",
- "LocalTitle",
- "OriginalTitle",
- "LockData",
- "LockedFields",
- "Format3D",
-
- // Deprecated. No longer saving in this field.
- "MPAARating",
-
- "MusicBrainzArtistId",
- "MusicBrainzAlbumArtistId",
- "MusicBrainzAlbumId",
- "MusicBrainzReleaseGroupId",
-
- // Deprecated. No longer saving in this field.
- "MusicbrainzId",
-
- "Overview",
- "Persons",
- "PremiereDate",
- "ProductionYear",
- "Rating",
- "RottenTomatoesId",
- "RunningTime",
-
- // Deprecated. No longer saving in this field.
- "Runtime",
-
- "SortTitle",
- "Studios",
- "Tags",
-
- // Deprecated. No longer saving in this field.
- "TagLine",
-
- "Taglines",
- "TMDbCollectionId",
- "TMDbId",
-
- // Deprecated. No longer saving in this field.
- "Trailer",
-
- "Trailers",
- "TVcomId",
- "TvDbId",
- "TVRageId",
- "Website",
- "Zap2ItId",
- "CollectionItems",
- "PlaylistItems",
- "Shares"
-
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
-
public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
{
FileSystem = fileSystem;
@@ -150,7 +64,7 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- public string GetSavePath(IHasMetadata item)
+ public string GetSavePath(BaseItem item)
{
return GetLocalSavePath(item);
}
@@ -160,14 +74,14 @@ namespace MediaBrowser.LocalMetadata.Savers
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- protected abstract string GetLocalSavePath(IHasMetadata item);
+ protected abstract string GetLocalSavePath(BaseItem item);
/// <summary>
/// Gets the name of the root element.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- protected virtual string GetRootElementName(IHasMetadata item)
+ protected virtual string GetRootElementName(BaseItem item)
{
return "Item";
}
@@ -178,14 +92,9 @@ namespace MediaBrowser.LocalMetadata.Savers
/// <param name="item">The item.</param>
/// <param name="updateType">Type of the update.</param>
/// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
- public abstract bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
-
- protected virtual List<string> GetTagsUsed()
- {
- return new List<string>();
- }
+ public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType);
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ public void Save(BaseItem item, CancellationToken cancellationToken)
{
var path = GetSavePath(item);
@@ -204,27 +113,15 @@ namespace MediaBrowser.LocalMetadata.Savers
private void SaveToFile(Stream stream, string path)
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
-
- var file = FileSystem.GetFileInfo(path);
-
- var wasHidden = false;
-
- // This will fail if the file is hidden
- if (file.Exists)
- {
- if (file.IsHidden)
- {
- wasHidden = true;
- }
- FileSystem.SetAttributes(path, false, false);
- }
+ // On Windows, savint the file will fail if the file is hidden or readonly
+ FileSystem.SetAttributes(path, false, false);
using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(filestream);
}
- if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden)
+ if (ConfigurationManager.Configuration.SaveMetadataHidden)
{
SetHidden(path, true);
}
@@ -242,7 +139,7 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- private void Save(IHasMetadata item, Stream stream, string xmlPath)
+ private void Save(BaseItem item, Stream stream, string xmlPath)
{
var settings = new XmlWriterSettings
{
@@ -259,7 +156,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteStartElement(root);
- var baseItem = item as BaseItem;
+ var baseItem = item;
if (baseItem != null)
{
@@ -268,32 +165,13 @@ namespace MediaBrowser.LocalMetadata.Savers
WriteCustomElements(item, writer);
- var tagsUsed = GetTagsUsed();
-
- try
- {
- AddCustomTags(xmlPath, tagsUsed, writer, Logger, FileSystem);
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (IOException)
- {
-
- }
- catch (XmlException ex)
- {
- Logger.ErrorException("Error reading existng xml", ex);
- }
-
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
- protected abstract void WriteCustomElements(IHasMetadata item, XmlWriter writer);
+ protected abstract void WriteCustomElements(BaseItem item, XmlWriter writer);
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
@@ -373,20 +251,16 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
+ if (item.RemoteTrailers.Length > 0)
{
- if (hasTrailers.RemoteTrailers.Length > 0)
- {
- writer.WriteStartElement("Trailers");
+ writer.WriteStartElement("Trailers");
- foreach (var trailer in hasTrailers.RemoteTrailers)
- {
- writer.WriteElementString("Trailer", trailer.Url);
- }
-
- writer.WriteEndElement();
+ foreach (var trailer in item.RemoteTrailers)
+ {
+ writer.WriteElementString("Trailer", trailer.Url);
}
+
+ writer.WriteEndElement();
}
if (item.ProductionLocations.Length > 0)
@@ -417,11 +291,6 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteElementString("ProductionYear", item.ProductionYear.Value.ToString(UsCulture));
}
- if (!string.IsNullOrEmpty(item.HomePageUrl))
- {
- writer.WriteElementString("Website", item.HomePageUrl);
- }
-
var hasAspectRatio = item as IHasAspectRatio;
if (hasAspectRatio != null)
{
@@ -447,7 +316,7 @@ namespace MediaBrowser.LocalMetadata.Savers
{
var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
- writer.WriteElementString("RunningTime", Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture));
+ writer.WriteElementString("RunningTime", Math.Floor(timespan.TotalMinutes).ToString(UsCulture));
}
if (item.ProviderIds != null)
@@ -469,7 +338,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- if (item.Genres.Count > 0)
+ if (item.Genres.Length > 0)
{
writer.WriteStartElement("Genres");
@@ -536,7 +405,7 @@ namespace MediaBrowser.LocalMetadata.Savers
}
var playlist = item as Playlist;
- if (playlist != null)
+ if (playlist != null && !Playlist.IsPlaylistFile(playlist.Path))
{
AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem");
}
@@ -617,10 +486,19 @@ namespace MediaBrowser.LocalMetadata.Savers
foreach (var link in items)
{
- if (!string.IsNullOrWhiteSpace(link.Path))
+ if (!string.IsNullOrWhiteSpace(link.Path) || !string.IsNullOrWhiteSpace(link.LibraryItemId))
{
writer.WriteStartElement(singularNodeName);
- writer.WriteElementString("Path", link.Path);
+ if (!string.IsNullOrWhiteSpace(link.Path))
+ {
+ writer.WriteElementString("Path", link.Path);
+ }
+
+ if (!string.IsNullOrWhiteSpace(link.LibraryItemId))
+ {
+ writer.WriteElementString("ItemId", link.LibraryItemId);
+ }
+
writer.WriteEndElement();
}
}
@@ -628,62 +506,9 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- private static bool IsPersonType(PersonInfo person, string type)
+ private bool IsPersonType(PersonInfo person, string type)
{
return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
}
-
- private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger, IFileSystem fileSystem)
- {
- var settings = XmlReaderSettingsFactory.Create(false);
-
- settings.CheckCharacters = false;
- settings.IgnoreProcessingInstructions = true;
- settings.IgnoreComments = true;
-
- using (var fileStream = fileSystem.OpenRead(path))
- {
- using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
- {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
- {
- try
- {
- reader.MoveToContent();
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error reading existing xml tags from {0}.", ex, path);
- return;
- }
-
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- var name = reader.Name;
-
- if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- writer.WriteNode(reader, false);
- }
- else
- {
- reader.Skip();
- }
- }
- else
- {
- reader.Read();
- }
- }
- }
- }
- }
- }
}
}
diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
index 214b44ae7..42a96588a 100644
--- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.LocalMetadata.Savers
{
public class BoxSetXmlSaver : BaseXmlSaver
{
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -27,11 +27,11 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is BoxSet && updateType >= ItemUpdateType.MetadataDownload;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
}
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "collection.xml");
}
diff --git a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
deleted file mode 100644
index d5d878ef7..000000000
--- a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading;
-using System.Xml;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Xml;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
- public class FolderXmlSaver : BaseXmlSaver
- {
- protected override string GetLocalSavePath(IHasMetadata item)
- {
- return Path.Combine(item.Path, "folder.xml");
- }
-
- protected override string GetRootElementName(IHasMetadata item)
- {
- return "Item";
- }
-
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- if (item is Folder)
- {
- if (!(item is Series) && !(item is BoxSet) && !(item is MusicArtist) && !(item is MusicAlbum) &&
- !(item is Season) &&
- !(item is GameSystem) &&
- !(item is Playlist))
- {
- return updateType >= ItemUpdateType.MetadataEdit;
- }
- }
-
- return false;
- }
-
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
- {
- }
-
- public FolderXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
- {
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
index 59b69746a..109b21d35 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.LocalMetadata.Savers
{
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -26,17 +26,7 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is GameSystem && updateType >= ItemUpdateType.MetadataDownload;
}
- protected override List<string> GetTagsUsed()
- {
- var list = new List<string>
- {
- "GameSystem"
- };
-
- return list;
- }
-
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var gameSystem = (GameSystem)item;
@@ -46,12 +36,12 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "gamesystem.xml");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "Item";
}
diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
index 24c5a4679..1a8e83e72 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
@@ -16,9 +16,9 @@ namespace MediaBrowser.LocalMetadata.Savers
/// </summary>
public class GameXmlSaver : BaseXmlSaver
{
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -28,18 +28,7 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is Game && updateType >= ItemUpdateType.MetadataDownload;
}
- protected override List<string> GetTagsUsed()
- {
- var list = new List<string>
- {
- "GameSystem",
- "Players"
- };
-
- return list;
- }
-
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var game = (Game)item;
@@ -53,12 +42,12 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return GetGameSavePath((Game)item);
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "Item";
}
diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
index ef28dde36..521a0b00e 100644
--- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.LocalMetadata.Savers
{
public class PlaylistXmlSaver : BaseXmlSaver
{
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -23,18 +23,7 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is Playlist && updateType >= ItemUpdateType.MetadataImport;
}
- protected override List<string> GetTagsUsed()
- {
- var list = new List<string>
- {
- "OwnerUserId",
- "PlaylistMediaType"
- };
-
- return list;
- }
-
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var game = (Playlist)item;
@@ -44,9 +33,21 @@ namespace MediaBrowser.LocalMetadata.Savers
}
}
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
+ {
+ return GetSavePath(item.Path, FileSystem);
+ }
+
+ public static string GetSavePath(string itemPath, IFileSystem fileSystem)
{
- return Path.Combine(item.Path, "playlist.xml");
+ var path = itemPath;
+
+ if (Playlist.IsPlaylistFile(path))
+ {
+ return Path.ChangeExtension(itemPath, ".xml");
+ }
+
+ return Path.Combine(path, "playlist.xml");
}
public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
diff --git a/MediaBrowser.Model/Activity/ActivityLogEntry.cs b/MediaBrowser.Model/Activity/ActivityLogEntry.cs
deleted file mode 100644
index 686578610..000000000
--- a/MediaBrowser.Model/Activity/ActivityLogEntry.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-
-namespace MediaBrowser.Model.Activity
-{
- public class ActivityLogEntry
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
-
- /// <summary>
- /// Gets or sets the short overview.
- /// </summary>
- /// <value>The short overview.</value>
- public string ShortOverview { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the date.
- /// </summary>
- /// <value>The date.</value>
- public DateTime Date { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the user primary image tag.
- /// </summary>
- /// <value>The user primary image tag.</value>
- public string UserPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the log severity.
- /// </summary>
- /// <value>The log severity.</value>
- public LogSeverity Severity { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
deleted file mode 100644
index 396be9573..000000000
--- a/MediaBrowser.Model/Activity/IActivityManager.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Activity
-{
- public interface IActivityManager
- {
- event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
-
- void Create(ActivityLogEntry entry);
-
- QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit);
- }
-}
diff --git a/MediaBrowser.Model/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs
deleted file mode 100644
index 8ee87ee2e..000000000
--- a/MediaBrowser.Model/Activity/IActivityRepository.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Activity
-{
- public interface IActivityRepository
- {
- void Create(ActivityLogEntry entry);
-
- QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit);
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs b/MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs
deleted file mode 100644
index a34165b23..000000000
--- a/MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace MediaBrowser.Model.ApiClient
-{
- public enum ConnectSignupResponse
- {
- Failure,
- Success,
- EmailInUse,
- UsernameInUser
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/GeneralCommandEventArgs.cs b/MediaBrowser.Model/ApiClient/GeneralCommandEventArgs.cs
deleted file mode 100644
index 3c0b09da8..000000000
--- a/MediaBrowser.Model/ApiClient/GeneralCommandEventArgs.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Class SystemCommandEventArgs
- /// </summary>
- public class GeneralCommandEventArgs
- {
- /// <summary>
- /// Gets or sets the command.
- /// </summary>
- /// <value>The command.</value>
- public GeneralCommand Command { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the known command.
- /// </summary>
- /// <value>The type of the known command.</value>
- public GeneralCommandType? KnownCommandType { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/HttpResponseEventArgs.cs b/MediaBrowser.Model/ApiClient/HttpResponseEventArgs.cs
deleted file mode 100644
index b1c19f056..000000000
--- a/MediaBrowser.Model/ApiClient/HttpResponseEventArgs.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Class HttpResponseEventArgs
- /// </summary>
- public class HttpResponseEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode StatusCode { get; set; }
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public Dictionary<string, string> Headers { get; set; }
-
- public HttpResponseEventArgs()
- {
- Headers = new Dictionary<string, string>();
- }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
deleted file mode 100644
index e2f780605..000000000
--- a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Model.ApiClient
-{
- public class ServerDiscoveryInfo
- {
- /// <summary>
- /// Gets or sets the address.
- /// </summary>
- /// <value>The address.</value>
- public string Address { get; set; }
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the endpoint address.
- /// </summary>
- /// <value>The endpoint address.</value>
- public string EndpointAddress { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/ApiClient/SessionUpdatesEventArgs.cs b/MediaBrowser.Model/ApiClient/SessionUpdatesEventArgs.cs
deleted file mode 100644
index af9a0986b..000000000
--- a/MediaBrowser.Model/ApiClient/SessionUpdatesEventArgs.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Model.ApiClient
-{
- /// <summary>
- /// Class SessionUpdatesEventArgs
- /// </summary>
- public class SessionUpdatesEventArgs
- {
- public SessionInfoDto[] Sessions { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/WakeOnLanInfo.cs b/MediaBrowser.Model/ApiClient/WakeOnLanInfo.cs
deleted file mode 100644
index f7a3c4ebb..000000000
--- a/MediaBrowser.Model/ApiClient/WakeOnLanInfo.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.ApiClient
-{
- public class WakeOnLanInfo
- {
- public string MacAddress { get; set; }
- public int Port { get; set; }
-
- public WakeOnLanInfo()
- {
- Port = 9;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
deleted file mode 100644
index 3b207d345..000000000
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Branding
-{
- public class BrandingOptions
- {
- /// <summary>
- /// Gets or sets the login disclaimer.
- /// </summary>
- /// <value>The login disclaimer.</value>
- public string LoginDisclaimer { get; set; }
- /// <summary>
- /// Gets or sets the custom CSS.
- /// </summary>
- /// <value>The custom CSS.</value>
- public string CustomCss { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Channels/AllChannelMediaQuery.cs b/MediaBrowser.Model/Channels/AllChannelMediaQuery.cs
deleted file mode 100644
index 920f3e4b2..000000000
--- a/MediaBrowser.Model/Channels/AllChannelMediaQuery.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Channels
-{
- public class AllChannelMediaQuery
- {
- /// <summary>
- /// Gets or sets the channel ids.
- /// </summary>
- /// <value>The channel ids.</value>
- public string[] ChannelIds { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { 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>
- /// Gets or sets the content types.
- /// </summary>
- /// <value>The content types.</value>
- public ChannelMediaContentType[] ContentTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the extra types.
- /// </summary>
- /// <value>The extra types.</value>
- public ExtraType[] ExtraTypes { get; set; }
- public TrailerType[] TrailerTypes { get; set; }
-
- public AllChannelMediaQuery()
- {
- ChannelIds = new string[] { };
-
- ContentTypes = new ChannelMediaContentType[] { };
- ExtraTypes = new ExtraType[] { };
- TrailerTypes = new TrailerType[] { };
-
- Filters = new ItemFilter[] { };
- Fields = new ItemFields[]{};
- }
-
- public ItemFilter[] Filters { get; set; }
- public ItemFields[] Fields { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs
deleted file mode 100644
index 39b40cabc..000000000
--- a/MediaBrowser.Model/Channels/ChannelFeatures.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Channels
-{
- public class ChannelFeatures
- {
- /// <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 a value indicating whether this instance can search.
- /// </summary>
- /// <value><c>true</c> if this instance can search; otherwise, <c>false</c>.</value>
- public bool CanSearch { get; set; }
-
- /// <summary>
- /// Gets or sets the media types.
- /// </summary>
- /// <value>The media types.</value>
- public ChannelMediaType[] MediaTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the content types.
- /// </summary>
- /// <value>The content types.</value>
- public ChannelMediaContentType[] ContentTypes { get; set; }
-
- /// <summary>
- /// Represents the maximum number of records the channel allows retrieving at a time
- /// </summary>
- public int? MaxPageSize { get; set; }
-
- /// <summary>
- /// Gets or sets the automatic refresh levels.
- /// </summary>
- /// <value>The automatic refresh levels.</value>
- public int? AutoRefreshLevels { get; set; }
-
- /// <summary>
- /// Gets or sets the default sort orders.
- /// </summary>
- /// <value>The default sort orders.</value>
- public ChannelItemSortField[] DefaultSortFields { get; set; }
-
- /// <summary>
- /// Indicates if a sort ascending/descending toggle is supported or not.
- /// </summary>
- public bool SupportsSortOrderToggle { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports latest media].
- /// </summary>
- /// <value><c>true</c> if [supports latest media]; otherwise, <c>false</c>.</value>
- public bool SupportsLatestMedia { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can filter.
- /// </summary>
- /// <value><c>true</c> if this instance can filter; otherwise, <c>false</c>.</value>
- public bool CanFilter { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports content downloading].
- /// </summary>
- /// <value><c>true</c> if [supports content downloading]; otherwise, <c>false</c>.</value>
- public bool SupportsContentDownloading { get; set; }
-
- public ChannelFeatures()
- {
- MediaTypes = new ChannelMediaType[] { };
- ContentTypes = new ChannelMediaContentType[] { };
-
- DefaultSortFields = new ChannelItemSortField[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Channels/ChannelFolderType.cs b/MediaBrowser.Model/Channels/ChannelFolderType.cs
deleted file mode 100644
index 7c97afd02..000000000
--- a/MediaBrowser.Model/Channels/ChannelFolderType.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Channels
-{
- public enum ChannelFolderType
- {
- Container = 0,
-
- MusicAlbum = 1,
-
- PhotoAlbum = 2,
-
- MusicArtist = 3,
-
- Series = 4,
-
- Season = 5
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelInfo.cs b/MediaBrowser.Model/Channels/ChannelInfo.cs
deleted file mode 100644
index 36e3c17d9..000000000
--- a/MediaBrowser.Model/Channels/ChannelInfo.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Channels
-{
- public class ChannelInfo
- {
- /// <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 home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- public string HomePageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the features.
- /// </summary>
- /// <value>The features.</value>
- public ChannelFeatures Features { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Channels/ChannelItemQuery.cs b/MediaBrowser.Model/Channels/ChannelItemQuery.cs
deleted file mode 100644
index 909d35b38..000000000
--- a/MediaBrowser.Model/Channels/ChannelItemQuery.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Channels
-{
- public class ChannelItemQuery
- {
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the category identifier.
- /// </summary>
- /// <value>The category identifier.</value>
- public string FolderId { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { 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; }
-
- public ItemFilter[] Filters { get; set; }
- public ItemFields[] Fields { get; set; }
- public Tuple<string, SortOrder>[] OrderBy { get; set; }
-
- public ChannelItemQuery()
- {
- Filters = new ItemFilter[] { };
- Fields = new ItemFields[] { };
- OrderBy = new Tuple<string, SortOrder>[] { };
- }
- }
-
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelItemSortField.cs b/MediaBrowser.Model/Channels/ChannelItemSortField.cs
deleted file mode 100644
index 6b5015b77..000000000
--- a/MediaBrowser.Model/Channels/ChannelItemSortField.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.Channels
-{
- public enum ChannelItemSortField
- {
- Name = 0,
- CommunityRating = 1,
- PremiereDate = 2,
- DateCreated = 3,
- Runtime = 4,
- PlayCount = 5,
- CommunityPlayCount = 6
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelMediaContentType.cs b/MediaBrowser.Model/Channels/ChannelMediaContentType.cs
deleted file mode 100644
index efb5021c0..000000000
--- a/MediaBrowser.Model/Channels/ChannelMediaContentType.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace MediaBrowser.Model.Channels
-{
- public enum ChannelMediaContentType
- {
- Clip = 0,
-
- Podcast = 1,
-
- Trailer = 2,
-
- Movie = 3,
-
- Episode = 4,
-
- Song = 5,
-
- MovieExtra = 6,
-
- TvExtra = 7,
-
- GameExtra = 8
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelMediaType.cs b/MediaBrowser.Model/Channels/ChannelMediaType.cs
deleted file mode 100644
index 102cb6644..000000000
--- a/MediaBrowser.Model/Channels/ChannelMediaType.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Channels
-{
- public enum ChannelMediaType
- {
- Audio = 0,
-
- Video = 1,
-
- Photo = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs
deleted file mode 100644
index b63d797f4..000000000
--- a/MediaBrowser.Model/Channels/ChannelQuery.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Channels
-{
- public class ChannelQuery
- {
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- public bool? EnableImages { get; set; }
- public int? ImageTypeLimit { get; set; }
- public ImageType[] EnableImageTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { 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>
- /// Gets or sets a value indicating whether [supports latest items].
- /// </summary>
- /// <value><c>true</c> if [supports latest items]; otherwise, <c>false</c>.</value>
- public bool? SupportsLatestItems { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
- public bool? IsFavorite { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Collections/CollectionCreationResult.cs b/MediaBrowser.Model/Collections/CollectionCreationResult.cs
deleted file mode 100644
index ad7ac9553..000000000
--- a/MediaBrowser.Model/Collections/CollectionCreationResult.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Model.Collections
-{
- public class CollectionCreationResult
- {
- public string Id { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/AccessSchedule.cs b/MediaBrowser.Model/Configuration/AccessSchedule.cs
deleted file mode 100644
index 3a66cf5bb..000000000
--- a/MediaBrowser.Model/Configuration/AccessSchedule.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class AccessSchedule
- {
- /// <summary>
- /// Gets or sets the day of week.
- /// </summary>
- /// <value>The day of week.</value>
- public DynamicDayOfWeek DayOfWeek { get; set; }
- /// <summary>
- /// Gets or sets the start hour.
- /// </summary>
- /// <value>The start hour.</value>
- public double StartHour { get; set; }
- /// <summary>
- /// Gets or sets the end hour.
- /// </summary>
- /// <value>The end hour.</value>
- public double EndHour { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
deleted file mode 100644
index b5b0101cb..000000000
--- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using MediaBrowser.Model.Updates;
-
-namespace MediaBrowser.Model.Configuration
-{
- /// <summary>
- /// Serves as a common base class for the Server and UI application Configurations
- /// ProtoInclude tells Protobuf about subclasses,
- /// The number 50 can be any number, so long as it doesn't clash with any of the ProtoMember numbers either here or in subclasses.
- /// </summary>
- public class BaseApplicationConfiguration
- {
- /// <summary>
- /// Gets or sets a value indicating whether [enable debug level logging].
- /// </summary>
- /// <value><c>true</c> if [enable debug level logging]; otherwise, <c>false</c>.</value>
- public bool EnableDebugLevelLogging { get; set; }
-
- /// <summary>
- /// Enable automatically and silently updating of the application
- /// </summary>
- /// <value><c>true</c> if [enable auto update]; otherwise, <c>false</c>.</value>
- public bool EnableAutoUpdate { get; set; }
-
- /// <summary>
- /// The number of days we should retain log files
- /// </summary>
- /// <value>The log file retention days.</value>
- public int LogFileRetentionDays { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [run at startup].
- /// </summary>
- /// <value><c>true</c> if [run at startup]; otherwise, <c>false</c>.</value>
- public bool RunAtStartup { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is first run.
- /// </summary>
- /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
- public bool IsStartupWizardCompleted { get; set; }
-
- /// <summary>
- /// Gets or sets the cache path.
- /// </summary>
- /// <value>The cache path.</value>
- public string CachePath { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="BaseApplicationConfiguration" /> class.
- /// </summary>
- public BaseApplicationConfiguration()
- {
- EnableAutoUpdate = true;
- LogFileRetentionDays = 3;
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/ChannelOptions.cs b/MediaBrowser.Model/Configuration/ChannelOptions.cs
deleted file mode 100644
index 9bd0ef9c5..000000000
--- a/MediaBrowser.Model/Configuration/ChannelOptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public class ChannelOptions
- {
- public int? PreferredStreamingWidth { get; set; }
- public string DownloadPath { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs b/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs
deleted file mode 100644
index c4b96ea2e..000000000
--- a/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class CinemaModeConfiguration
- {
- public bool EnableIntrosForMovies { get; set; }
- public bool EnableIntrosForEpisodes { get; set; }
- public bool EnableIntrosForWatchedContent { get; set; }
- public bool EnableIntrosFromUpcomingTrailers { get; set; }
- public bool EnableIntrosFromMoviesInLibrary { get; set; }
- public bool EnableIntrosParentalControl { get; set; }
- public bool EnableIntrosFromSimilarMovies { get; set; }
- public string CustomIntroPath { get; set; }
- public string MediaInfoIntroPath { get; set; }
- public bool EnableIntrosFromUpcomingDvdMovies { get; set; }
- public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }
-
- public int TrailerLimit { get; set; }
-
- public CinemaModeConfiguration()
- {
- EnableIntrosParentalControl = true;
- EnableIntrosFromSimilarMovies = true;
- TrailerLimit = 2;
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs b/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
deleted file mode 100644
index 1c7de11fd..000000000
--- a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public enum DynamicDayOfWeek
- {
- Sunday = 0,
- Monday = 1,
- Tuesday = 2,
- Wednesday = 3,
- Thursday = 4,
- Friday = 5,
- Saturday = 6,
- Everyday = 7,
- Weekday = 8,
- Weekend = 9
- }
-}
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
deleted file mode 100644
index fbc5e1b37..000000000
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class EncodingOptions
- {
- public int EncodingThreadCount { get; set; }
- public string TranscodingTempPath { get; set; }
- public double DownMixAudioBoost { get; set; }
- public bool EnableThrottling { get; set; }
- public int ThrottleDelaySeconds { get; set; }
- public string HardwareAccelerationType { get; set; }
- public string EncoderAppPath { get; set; }
- public string VaapiDevice { get; set; }
- public int H264Crf { get; set; }
- public string H264Preset { get; set; }
- public string DeinterlaceMethod { get; set; }
- public bool EnableHardwareEncoding { get; set; }
- public bool EnableSubtitleExtraction { get; set; }
-
- public string[] HardwareDecodingCodecs { get; set; }
-
- public EncodingOptions()
- {
- DownMixAudioBoost = 2;
- EnableThrottling = true;
- ThrottleDelaySeconds = 180;
- EncodingThreadCount = -1;
- // This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
- VaapiDevice = "/dev/dri/renderD128";
- H264Crf = 23;
- EnableHardwareEncoding = true;
- EnableSubtitleExtraction = true;
- HardwareDecodingCodecs = new string[] { "h264", "vc1" };
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/FanartOptions.cs b/MediaBrowser.Model/Configuration/FanartOptions.cs
deleted file mode 100644
index 6924b25d7..000000000
--- a/MediaBrowser.Model/Configuration/FanartOptions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class FanartOptions
- {
- /// <summary>
- /// Gets or sets the user API key.
- /// </summary>
- /// <value>The user API key.</value>
- public string UserApiKey { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/ImageOption.cs b/MediaBrowser.Model/Configuration/ImageOption.cs
deleted file mode 100644
index ade0af83e..000000000
--- a/MediaBrowser.Model/Configuration/ImageOption.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Configuration
-{
- public class ImageOption
- {
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public ImageType Type { get; set; }
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int Limit { get; set; }
-
- /// <summary>
- /// Gets or sets the minimum width.
- /// </summary>
- /// <value>The minimum width.</value>
- public int MinWidth { get; set; }
-
- public ImageOption()
- {
- Limit = 1;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
deleted file mode 100644
index 611678e67..000000000
--- a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public enum ImageSavingConvention
- {
- Legacy,
- Compatible
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
deleted file mode 100644
index 07a821baf..000000000
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public class LibraryOptions
- {
- public bool EnableArchiveMediaFiles { get; set; }
- public bool EnablePhotos { get; set; }
- public bool EnableRealtimeMonitor { get; set; }
- public bool EnableChapterImageExtraction { get; set; }
- public bool ExtractChapterImagesDuringLibraryScan { get; set; }
- public bool DownloadImagesInAdvance { get; set; }
- public MediaPathInfo[] PathInfos { get; set; }
-
- public bool SaveLocalMetadata { get; set; }
- public bool EnableInternetProviders { get; set; }
- public bool ImportMissingEpisodes { get; set; }
- public bool EnableAutomaticSeriesGrouping { get; set; }
- public bool EnableEmbeddedTitles { get; set; }
-
- public int AutomaticRefreshIntervalDays { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred metadata language.
- /// </summary>
- /// <value>The preferred metadata language.</value>
- public string PreferredMetadataLanguage { get; set; }
-
- /// <summary>
- /// Gets or sets the metadata country code.
- /// </summary>
- /// <value>The metadata country code.</value>
- public string MetadataCountryCode { get; set; }
-
- public string SeasonZeroDisplayName { get; set; }
-
- public LibraryOptions()
- {
- EnablePhotos = true;
- EnableRealtimeMonitor = true;
- PathInfos = new MediaPathInfo[] { };
- EnableInternetProviders = true;
- EnableAutomaticSeriesGrouping = true;
- SeasonZeroDisplayName = "Specials";
- }
- }
-
- public class MediaPathInfo
- {
- public string Path { get; set; }
- public string NetworkPath { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs
deleted file mode 100644
index d1658e5d6..000000000
--- a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class MetadataConfiguration
- {
- public bool UseFileCreationTimeForDateAdded { get; set; }
-
- public MetadataConfiguration()
- {
- UseFileCreationTimeForDateAdded = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs
deleted file mode 100644
index 8a41decbf..000000000
--- a/MediaBrowser.Model/Configuration/MetadataOptions.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Configuration
-{
- /// <summary>
- /// Class MetadataOptions.
- /// </summary>
- public class MetadataOptions
- {
- public string ItemType { get; set; }
-
- public ImageOption[] ImageOptions { get; set; }
-
- public string[] DisabledMetadataSavers { get; set; }
- public string[] LocalMetadataReaderOrder { get; set; }
-
- public string[] DisabledMetadataFetchers { get; set; }
- public string[] MetadataFetcherOrder { get; set; }
-
- public string[] DisabledImageFetchers { get; set; }
- public string[] ImageFetcherOrder { get; set; }
-
- public MetadataOptions()
- : this(3, 1280)
- {
- }
-
- public MetadataOptions(int backdropLimit, int minBackdropWidth)
- {
- ImageOptions = new[]
- {
- new ImageOption
- {
- Limit = backdropLimit,
- MinWidth = minBackdropWidth,
- Type = ImageType.Backdrop
- }
- };
-
- DisabledMetadataSavers = new string[] { };
- LocalMetadataReaderOrder = new string[] { };
-
- DisabledMetadataFetchers = new string[] { };
- MetadataFetcherOrder = new string[] { };
- DisabledImageFetchers = new string[] { };
- ImageFetcherOrder = new string[] { };
- }
-
- public int GetLimit(ImageType type)
- {
- ImageOption option = null;
- foreach (ImageOption i in ImageOptions)
- {
- if (i.Type == type)
- {
- option = i;
- break;
- }
- }
-
- return option == null ? 1 : option.Limit;
- }
-
- public int GetMinWidth(ImageType type)
- {
- ImageOption option = null;
- foreach (ImageOption i in ImageOptions)
- {
- if (i.Type == type)
- {
- option = i;
- break;
- }
- }
-
- return option == null ? 0 : option.MinWidth;
- }
-
- public bool IsEnabled(ImageType type)
- {
- return GetLimit(type) > 0;
- }
-
- public bool IsMetadataSaverEnabled(string name)
- {
- return !ListHelper.ContainsIgnoreCase(DisabledMetadataSavers, name);
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/MetadataPlugin.cs b/MediaBrowser.Model/Configuration/MetadataPlugin.cs
deleted file mode 100644
index f3e0ce106..000000000
--- a/MediaBrowser.Model/Configuration/MetadataPlugin.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public class MetadataPlugin
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public MetadataPluginType Type { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
deleted file mode 100644
index 80142cf43..000000000
--- a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Configuration
-{
- public class MetadataPluginSummary
- {
- /// <summary>
- /// Gets or sets the type of the item.
- /// </summary>
- /// <value>The type of the item.</value>
- public string ItemType { get; set; }
-
- /// <summary>
- /// Gets or sets the plugins.
- /// </summary>
- /// <value>The plugins.</value>
- public MetadataPlugin[] Plugins { get; set; }
-
- /// <summary>
- /// Gets or sets the supported image types.
- /// </summary>
- /// <value>The supported image types.</value>
- public ImageType[] SupportedImageTypes { get; set; }
-
- public MetadataPluginSummary()
- {
- SupportedImageTypes = new ImageType[] { };
- Plugins = new MetadataPlugin[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginType.cs b/MediaBrowser.Model/Configuration/MetadataPluginType.cs
deleted file mode 100644
index 95ca3b2e6..000000000
--- a/MediaBrowser.Model/Configuration/MetadataPluginType.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- /// <summary>
- /// Enum MetadataPluginType
- /// </summary>
- public enum MetadataPluginType
- {
- LocalImageProvider,
- ImageFetcher,
- ImageSaver,
- LocalMetadataProvider,
- MetadataFetcher,
- MetadataSaver
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
deleted file mode 100644
index 41ed0648a..000000000
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ /dev/null
@@ -1,574 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Configuration
-{
- /// <summary>
- /// Represents the server configuration.
- /// </summary>
- public class ServerConfiguration : BaseApplicationConfiguration
- {
- public const int DefaultHttpPort = 8096;
- public const int DefaultHttpsPort = 8920;
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable u pn p].
- /// </summary>
- /// <value><c>true</c> if [enable u pn p]; otherwise, <c>false</c>.</value>
- public bool EnableUPnP { get; set; }
-
- /// <summary>
- /// Gets or sets the public mapped port.
- /// </summary>
- /// <value>The public mapped port.</value>
- public int PublicPort { get; set; }
-
- /// <summary>
- /// Gets or sets the public HTTPS port.
- /// </summary>
- /// <value>The public HTTPS port.</value>
- public int PublicHttpsPort { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTP server port number.
- /// </summary>
- /// <value>The HTTP server port number.</value>
- public int HttpServerPortNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTPS server port number.
- /// </summary>
- /// <value>The HTTPS server port number.</value>
- public int HttpsPortNumber { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [use HTTPS].
- /// </summary>
- /// <value><c>true</c> if [use HTTPS]; otherwise, <c>false</c>.</value>
- public bool EnableHttps { get; set; }
- public bool EnableNormalizedItemByNameIds { get; set; }
-
- /// <summary>
- /// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
- /// </summary>
- /// <value>The value pointing to the file system where the ssl certiifcate is located..</value>
- public string CertificatePath { get; set; }
- public string CertificatePassword { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is port authorized.
- /// </summary>
- /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value>
- public bool IsPortAuthorized { get; set; }
-
- public bool AutoRunWebApp { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable case sensitive item ids].
- /// </summary>
- /// <value><c>true</c> if [enable case sensitive item ids]; otherwise, <c>false</c>.</value>
- public bool EnableCaseSensitiveItemIds { get; set; }
-
- public bool DisableLiveTvChannelUserDataName { get; set; }
-
- /// <summary>
- /// Gets or sets the metadata path.
- /// </summary>
- /// <value>The metadata path.</value>
- public string MetadataPath { get; set; }
- public string MetadataNetworkPath { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred metadata language.
- /// </summary>
- /// <value>The preferred metadata language.</value>
- public string PreferredMetadataLanguage { get; set; }
-
- /// <summary>
- /// Gets or sets the metadata country code.
- /// </summary>
- /// <value>The metadata country code.</value>
- public string MetadataCountryCode { get; set; }
-
- /// <summary>
- /// Characters to be replaced with a ' ' in strings to create a sort name
- /// </summary>
- /// <value>The sort replace characters.</value>
- public string[] SortReplaceCharacters { get; set; }
-
- /// <summary>
- /// Characters to be removed from strings to create a sort name
- /// </summary>
- /// <value>The sort remove characters.</value>
- public string[] SortRemoveCharacters { get; set; }
-
- /// <summary>
- /// Words to be removed from strings to create a sort name
- /// </summary>
- /// <value>The sort remove words.</value>
- public string[] SortRemoveWords { get; set; }
-
- /// <summary>
- /// Gets or sets the minimum percentage of an item that must be played in order for playstate to be updated.
- /// </summary>
- /// <value>The min resume PCT.</value>
- public int MinResumePct { get; set; }
-
- /// <summary>
- /// Gets or sets the maximum percentage of an item that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched.
- /// </summary>
- /// <value>The max resume PCT.</value>
- public int MaxResumePct { get; set; }
-
- /// <summary>
- /// Gets or sets the minimum duration that an item must have in order to be eligible for playstate updates..
- /// </summary>
- /// <value>The min resume duration seconds.</value>
- public int MinResumeDurationSeconds { get; set; }
-
- /// <summary>
- /// The delay in seconds that we will wait after a file system change to try and discover what has been added/removed
- /// Some delay is necessary with some items because their creation is not atomic. It involves the creation of several
- /// different directories and files.
- /// </summary>
- /// <value>The file watcher delay.</value>
- public int LibraryMonitorDelay { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable dashboard response caching].
- /// Allows potential contributors without visual studio to modify production dashboard code and test changes.
- /// </summary>
- /// <value><c>true</c> if [enable dashboard response caching]; otherwise, <c>false</c>.</value>
- public bool EnableDashboardResponseCaching { get; set; }
-
- /// <summary>
- /// Allows the dashboard to be served from a custom path.
- /// </summary>
- /// <value>The dashboard source path.</value>
- public string DashboardSourcePath { get; set; }
-
- /// <summary>
- /// Gets or sets the image saving convention.
- /// </summary>
- /// <value>The image saving convention.</value>
- public ImageSavingConvention ImageSavingConvention { get; set; }
-
- public MetadataOptions[] MetadataOptions { get; set; }
-
- public bool EnableAutomaticRestart { get; set; }
- public bool SkipDeserializationForBasicTypes { get; set; }
-
- public string ServerName { get; set; }
- public string WanDdns { get; set; }
-
- public string UICulture { get; set; }
-
- public bool SaveMetadataHidden { get; set; }
-
- public NameValuePair[] ContentTypes { get; set; }
-
- public int RemoteClientBitrateLimit { get; set; }
-
- public int SharingExpirationDays { get; set; }
-
- public int SchemaVersion { get; set; }
-
- public bool EnableAnonymousUsageReporting { get; set; }
- public bool EnableFolderView { get; set; }
- public bool EnableGroupingIntoCollections { get; set; }
- public bool DisplaySpecialsWithinSeasons { get; set; }
- public bool DisplayCollectionsView { get; set; }
- public string[] LocalNetworkAddresses { get; set; }
- public string[] CodecsUsed { get; set; }
- public bool EnableChannelView { get; set; }
- public bool EnableExternalContentInSuggestions { get; set; }
- public bool RequireHttps { get; set; }
- public bool IsBehindProxy { get; set; }
- public bool EnableNewOmdbSupport { get; set; }
-
- public int ImageExtractionTimeoutMs { get; set; }
-
- public PathSubstitution[] PathSubstitutions { get; set; }
- public bool EnableSimpleArtistDetection { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
- /// </summary>
- public ServerConfiguration()
- {
- LocalNetworkAddresses = new string[] { };
- CodecsUsed = new string[] { };
- ImageExtractionTimeoutMs = 0;
- PathSubstitutions = new PathSubstitution[] { };
- EnableSimpleArtistDetection = true;
-
- DisplaySpecialsWithinSeasons = true;
- EnableExternalContentInSuggestions = true;
-
- ImageSavingConvention = ImageSavingConvention.Compatible;
- PublicPort = DefaultHttpPort;
- PublicHttpsPort = DefaultHttpsPort;
- HttpServerPortNumber = DefaultHttpPort;
- HttpsPortNumber = DefaultHttpsPort;
- EnableHttps = false;
- EnableDashboardResponseCaching = true;
- EnableAnonymousUsageReporting = true;
- EnableCaseSensitiveItemIds = true;
-
- EnableAutomaticRestart = true;
-
- EnableUPnP = true;
- SharingExpirationDays = 30;
- MinResumePct = 5;
- MaxResumePct = 90;
-
- // 5 minutes
- MinResumeDurationSeconds = 300;
-
- LibraryMonitorDelay = 60;
-
- ContentTypes = new NameValuePair[] { };
-
- PreferredMetadataLanguage = "en";
- MetadataCountryCode = "US";
-
- SortReplaceCharacters = new[] { ".", "+", "%" };
- SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" };
- SortRemoveWords = new[] { "the", "a", "an" };
-
- UICulture = "en-us";
-
- MetadataOptions = new[]
- {
- new MetadataOptions(1, 1280) {ItemType = "Book"},
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "Movie",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 1,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Art
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Disc
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- },
-
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Banner
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Thumb
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Logo
- }
- }
- },
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "MusicVideo",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 1,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Art
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Disc
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- },
-
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Banner
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Thumb
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Logo
- }
- },
- DisabledMetadataFetchers = new []{ "The Open Movie Database" },
- DisabledImageFetchers = new []{ "The Open Movie Database", "FanArt" }
- },
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "Series",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 1,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Art
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Banner
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Thumb
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Logo
- }
- },
- DisabledMetadataFetchers = new []{ "TheMovieDb" },
- DisabledImageFetchers = new []{ "TheMovieDb" }
- },
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "MusicAlbum",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 0,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Disc
- }
- },
- DisabledMetadataFetchers = new []{ "TheAudioDB" }
- },
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "MusicArtist",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 1,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- // Don't download this by default
- // They do look great, but most artists won't have them, which means a banner view isn't really possible
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Banner
- },
-
- // Don't download this by default
- // Generally not used
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Art
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Logo
- }
- },
- DisabledMetadataFetchers = new []{ "TheAudioDB" }
- },
-
- new MetadataOptions(1, 1280)
- {
- ItemType = "BoxSet",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 1,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Thumb
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Logo
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Art
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Disc
- },
-
- // Don't download this by default as it's rarely used.
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Banner
- }
- }
- },
-
- new MetadataOptions(0, 1280)
- {
- ItemType = "Season",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 0,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- },
-
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Banner
- },
-
- new ImageOption
- {
- Limit = 0,
- Type = ImageType.Thumb
- }
- },
- DisabledMetadataFetchers = new []{ "TheMovieDb" },
- DisabledImageFetchers = new [] { "FanArt" }
- },
-
- new MetadataOptions(0, 1280)
- {
- ItemType = "Episode",
- ImageOptions = new []
- {
- new ImageOption
- {
- Limit = 0,
- MinWidth = 1280,
- Type = ImageType.Backdrop
- },
-
- new ImageOption
- {
- Limit = 1,
- Type = ImageType.Primary
- }
- },
- DisabledMetadataFetchers = new []{ "The Open Movie Database", "TheMovieDb" },
- DisabledImageFetchers = new []{ "The Open Movie Database", "TheMovieDb" }
- }
- };
- }
- }
-
- public class PathSubstitution
- {
- public string From { get; set; }
- public string To { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
deleted file mode 100644
index fbee912d9..000000000
--- a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public enum SubtitlePlaybackMode
- {
- Default = 0,
- Always = 1,
- OnlyForced = 2,
- None = 3,
- Smart = 4
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/UnratedItem.cs b/MediaBrowser.Model/Configuration/UnratedItem.cs
deleted file mode 100644
index 1082d684b..000000000
--- a/MediaBrowser.Model/Configuration/UnratedItem.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public enum UnratedItem
- {
- Movie,
- Trailer,
- Series,
- Music,
- Game,
- Book,
- LiveTvChannel,
- LiveTvProgram,
- ChannelContent,
- Other
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
deleted file mode 100644
index 15bd003ae..000000000
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- /// <summary>
- /// Class UserConfiguration
- /// </summary>
- public class UserConfiguration
- {
- /// <summary>
- /// Gets or sets the audio language preference.
- /// </summary>
- /// <value>The audio language preference.</value>
- public string AudioLanguagePreference { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [play default audio track].
- /// </summary>
- /// <value><c>true</c> if [play default audio track]; otherwise, <c>false</c>.</value>
- public bool PlayDefaultAudioTrack { get; set; }
-
- /// <summary>
- /// Gets or sets the subtitle language preference.
- /// </summary>
- /// <value>The subtitle language preference.</value>
- public string SubtitleLanguagePreference { get; set; }
-
- public bool DisplayMissingEpisodes { get; set; }
-
- public string[] GroupedFolders { get; set; }
-
- public SubtitlePlaybackMode SubtitleMode { get; set; }
- public bool DisplayCollectionsView { get; set; }
-
- public bool EnableLocalPassword { get; set; }
-
- public string[] OrderedViews { get; set; }
-
- public string[] LatestItemsExcludes { get; set; }
-
- public bool HidePlayedInLatest { get; set; }
-
- public bool RememberAudioSelections { get; set; }
- public bool RememberSubtitleSelections { get; set; }
- public bool EnableNextEpisodeAutoPlay { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
- /// </summary>
- public UserConfiguration()
- {
- EnableNextEpisodeAutoPlay = true;
- RememberAudioSelections = true;
- RememberSubtitleSelections = true;
-
- HidePlayedInLatest = true;
- PlayDefaultAudioTrack = true;
-
- LatestItemsExcludes = new string[] { };
- OrderedViews = new string[] { };
-
- GroupedFolders = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
deleted file mode 100644
index de8a59a3d..000000000
--- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-namespace MediaBrowser.Model.Configuration
-{
- public class XbmcMetadataOptions
- {
- public string UserId { get; set; }
-
- public string ReleaseDateFormat { get; set; }
-
- public bool SaveImagePathsInNfo { get; set; }
- public bool EnablePathSubstitution { get; set; }
-
- public bool EnableExtraThumbsDuplication { get; set; }
-
- public XbmcMetadataOptions()
- {
- ReleaseDateFormat = "yyyy-MM-dd";
-
- SaveImagePathsInNfo = true;
- EnablePathSubstitution = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs b/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs
deleted file mode 100644
index c60224045..000000000
--- a/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectAuthenticationExchangeResult
- {
- /// <summary>
- /// Gets or sets the local user identifier.
- /// </summary>
- /// <value>The local user identifier.</value>
- public string LocalUserId { get; set; }
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- /// <value>The access token.</value>
- public string AccessToken { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs b/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs
deleted file mode 100644
index 0df035e57..000000000
--- a/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectAuthenticationResult
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public ConnectUser User { get; set; }
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- /// <value>The access token.</value>
- public string AccessToken { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthorization.cs b/MediaBrowser.Model/Connect/ConnectAuthorization.cs
deleted file mode 100644
index e8baf7269..000000000
--- a/MediaBrowser.Model/Connect/ConnectAuthorization.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectAuthorization
- {
- public string ConnectUserId { get; set; }
- public string UserName { get; set; }
- public string ImageUrl { get; set; }
- public string Id { get; set; }
- public string[] EnabledLibraries { get; set; }
- public bool EnableLiveTv { get; set; }
- public string[] EnabledChannels { get; set; }
-
- public ConnectAuthorization()
- {
- EnabledLibraries = new string[] { };
- EnabledChannels = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs b/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs
deleted file mode 100644
index 6baea15a9..000000000
--- a/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectAuthorizationRequest
- {
- public string SendingUserId { get; set; }
- public string ConnectUserName { get; set; }
- public string[] EnabledLibraries { get; set; }
- public bool EnableLiveTv { get; set; }
- public string[] EnabledChannels { get; set; }
-
- public ConnectAuthorizationRequest()
- {
- EnabledLibraries = new string[] { };
- EnabledChannels = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectPassword.cs b/MediaBrowser.Model/Connect/ConnectPassword.cs
deleted file mode 100644
index 6ed9f0339..000000000
--- a/MediaBrowser.Model/Connect/ConnectPassword.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public static class ConnectPassword
- {
- public static string PerformPreHashFilter(string password)
- {
- return password
- .Replace("&", "&amp;")
- .Replace("/", "&#092;")
- .Replace("!", "&#33;")
- .Replace("$", "&#036;")
- .Replace("\"", "&quot;")
- .Replace("<", "&lt;")
- .Replace(">", "&gt;")
- .Replace("'", "&#39;");
- }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectUser.cs b/MediaBrowser.Model/Connect/ConnectUser.cs
deleted file mode 100644
index da290da12..000000000
--- a/MediaBrowser.Model/Connect/ConnectUser.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectUser
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public string Email { get; set; }
- public bool IsActive { get; set; }
- public string ImageUrl { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectUserQuery.cs b/MediaBrowser.Model/Connect/ConnectUserQuery.cs
deleted file mode 100644
index a7dc649a8..000000000
--- a/MediaBrowser.Model/Connect/ConnectUserQuery.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectUserQuery
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public string Email { get; set; }
- public string NameOrEmail { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/ConnectUserServer.cs b/MediaBrowser.Model/Connect/ConnectUserServer.cs
deleted file mode 100644
index caa6ebb14..000000000
--- a/MediaBrowser.Model/Connect/ConnectUserServer.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class ConnectUserServer
- {
- public string Id { get; set; }
- public string Url { get; set; }
- public string Name { get; set; }
- public string AccessKey { get; set; }
- public string SystemId { get; set; }
- public string LocalAddress { get; set; }
- public string UserType { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/PinCreationResult.cs b/MediaBrowser.Model/Connect/PinCreationResult.cs
deleted file mode 100644
index c96e92049..000000000
--- a/MediaBrowser.Model/Connect/PinCreationResult.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class PinCreationResult
- {
- public string Pin { get; set; }
- public string DeviceId { get; set; }
- public bool IsConfirmed { get; set; }
- public bool IsExpired { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/PinExchangeResult.cs b/MediaBrowser.Model/Connect/PinExchangeResult.cs
deleted file mode 100644
index d1a184e8c..000000000
--- a/MediaBrowser.Model/Connect/PinExchangeResult.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class PinExchangeResult
- {
- public string UserId { get; set; }
- public string AccessToken { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/PinStatusResult.cs b/MediaBrowser.Model/Connect/PinStatusResult.cs
deleted file mode 100644
index de60c463d..000000000
--- a/MediaBrowser.Model/Connect/PinStatusResult.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public class PinStatusResult
- {
- public string Pin { get; set; }
- public bool IsConfirmed { get; set; }
- public bool IsExpired { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Connect/UserLinkType.cs b/MediaBrowser.Model/Connect/UserLinkType.cs
deleted file mode 100644
index 4ac5bfde1..000000000
--- a/MediaBrowser.Model/Connect/UserLinkType.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Model.Connect
-{
- public enum UserLinkType
- {
- /// <summary>
- /// The linked user
- /// </summary>
- LinkedUser = 0,
- /// <summary>
- /// The guest
- /// </summary>
- Guest = 1
- }
-}
diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
deleted file mode 100644
index 7a82dee52..000000000
--- a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.IO;
-
-namespace MediaBrowser.Model.Cryptography
-{
- public interface ICryptoProvider
- {
- Guid GetMD5(string str);
- byte[] ComputeMD5(Stream str);
- byte[] ComputeMD5(byte[] bytes);
- byte[] ComputeSHA1(byte[] bytes);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Devices/ContentUploadHistory.cs b/MediaBrowser.Model/Devices/ContentUploadHistory.cs
deleted file mode 100644
index 2b344df24..000000000
--- a/MediaBrowser.Model/Devices/ContentUploadHistory.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Devices
-{
- public class ContentUploadHistory
- {
- public string DeviceId { get; set; }
- public LocalFileInfo[] FilesUploaded { get; set; }
-
- public ContentUploadHistory()
- {
- FilesUploaded = new LocalFileInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs
deleted file mode 100644
index d2ee3fdcc..000000000
--- a/MediaBrowser.Model/Devices/DeviceInfo.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using MediaBrowser.Model.Session;
-using System;
-
-namespace MediaBrowser.Model.Devices
-{
- public class DeviceInfo
- {
- /// <summary>
- /// Gets or sets the name of the reported.
- /// </summary>
- /// <value>The name of the reported.</value>
- public string ReportedName { get; set; }
- /// <summary>
- /// Gets or sets the name of the custom.
- /// </summary>
- /// <value>The name of the custom.</value>
- public string CustomName { get; set; }
- /// <summary>
- /// Gets or sets the camera upload path.
- /// </summary>
- /// <value>The camera upload path.</value>
- public string CameraUploadPath { get; set; }
-
- 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 last name of the user.
- /// </summary>
- /// <value>The last name of the user.</value>
- public string LastUserName { get; set; }
- /// <summary>
- /// Gets or sets the name of the application.
- /// </summary>
- /// <value>The name of the application.</value>
- public string AppName { get; set; }
- /// <summary>
- /// Gets or sets the application version.
- /// </summary>
- /// <value>The application version.</value>
- public string AppVersion { get; set; }
- /// <summary>
- /// Gets or sets the last user identifier.
- /// </summary>
- /// <value>The last user identifier.</value>
- public string LastUserId { get; set; }
- /// <summary>
- /// Gets or sets the date last modified.
- /// </summary>
- /// <value>The date last modified.</value>
- public DateTime DateLastModified { get; set; }
- /// <summary>
- /// Gets or sets the capabilities.
- /// </summary>
- /// <value>The capabilities.</value>
- public ClientCapabilities Capabilities { get; set; }
-
- public DeviceInfo()
- {
- Capabilities = new ClientCapabilities();
- }
- }
-}
diff --git a/MediaBrowser.Model/Devices/DeviceOptions.cs b/MediaBrowser.Model/Devices/DeviceOptions.cs
deleted file mode 100644
index 2524a2f99..000000000
--- a/MediaBrowser.Model/Devices/DeviceOptions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Devices
-{
- public class DeviceOptions
- {
- /// <summary>
- /// Gets or sets the name of the custom.
- /// </summary>
- /// <value>The name of the custom.</value>
- public string CustomName { get; set; }
- /// <summary>
- /// Gets or sets the camera upload path.
- /// </summary>
- /// <value>The camera upload path.</value>
- public string CameraUploadPath { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs
deleted file mode 100644
index 9ceea1ea8..000000000
--- a/MediaBrowser.Model/Devices/DeviceQuery.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-namespace MediaBrowser.Model.Devices
-{
- public class DeviceQuery
- {
- /// <summary>
- /// Gets or sets a value indicating whether [supports unique identifier].
- /// </summary>
- /// <value><c>null</c> if [supports unique identifier] contains no value, <c>true</c> if [supports unique identifier]; otherwise, <c>false</c>.</value>
- public bool? SupportsPersistentIdentifier { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [supports synchronize].
- /// </summary>
- /// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value>
- public bool? SupportsSync { get; set; }
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Devices/DevicesOptions.cs b/MediaBrowser.Model/Devices/DevicesOptions.cs
deleted file mode 100644
index ba2baddbe..000000000
--- a/MediaBrowser.Model/Devices/DevicesOptions.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Model.Devices
-{
- public class DevicesOptions
- {
- public string[] EnabledCameraUploadDevices { get; set; }
- public string CameraUploadPath { get; set; }
- public bool EnableCameraUploadSubfolders { get; set; }
-
- public DevicesOptions()
- {
- EnabledCameraUploadDevices = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Devices/LocalFileInfo.cs b/MediaBrowser.Model/Devices/LocalFileInfo.cs
deleted file mode 100644
index e7a78bf8b..000000000
--- a/MediaBrowser.Model/Devices/LocalFileInfo.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.Devices
-{
- public class LocalFileInfo
- {
- public string Name { get; set; }
- public string Id { get; set; }
- public string Album { get; set; }
- public string MimeType { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Diagnostics/IProcess.cs b/MediaBrowser.Model/Diagnostics/IProcess.cs
deleted file mode 100644
index 7cd26af00..000000000
--- a/MediaBrowser.Model/Diagnostics/IProcess.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Diagnostics
-{
- public interface IProcess : IDisposable
- {
- event EventHandler Exited;
-
- void Kill();
- bool WaitForExit(int timeMs);
- Task<bool> WaitForExitAsync(int timeMs);
- int ExitCode { get; }
- void Start();
- StreamWriter StandardInput { get; }
- StreamReader StandardError { get; }
- StreamReader StandardOutput { get; }
- ProcessOptions StartInfo { get; }
- }
-}
diff --git a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
deleted file mode 100644
index a60c4b4fb..000000000
--- a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Diagnostics
-{
- public interface IProcessFactory
- {
- IProcess Create(ProcessOptions options);
- }
-
- public class ProcessOptions
- {
- public String FileName { get; set; }
- public String Arguments { get; set; }
- public String WorkingDirectory { get; set; }
- public bool CreateNoWindow { get; set; }
- public bool UseShellExecute { get; set; }
- public bool EnableRaisingEvents { get; set; }
- public bool ErrorDialog { get; set; }
- public bool RedirectStandardError { get; set; }
- public bool RedirectStandardInput { get; set; }
- public bool RedirectStandardOutput { get; set; }
- public bool IsHidden { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
deleted file mode 100644
index 6584bb3cc..000000000
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Dlna
-{
- /// <summary>
- /// Class AudioOptions.
- /// </summary>
- public class AudioOptions
- {
- public AudioOptions()
- {
- Context = EncodingContext.Streaming;
-
- EnableDirectPlay = true;
- EnableDirectStream = true;
- }
-
- public bool EnableDirectPlay { get; set; }
- public bool EnableDirectStream { get; set; }
- public bool ForceDirectPlay { get; set; }
- public bool ForceDirectStream { get; set; }
-
- public string ItemId { get; set; }
- public MediaSourceInfo[] MediaSources { get; set; }
- public DeviceProfile Profile { get; set; }
-
- /// <summary>
- /// Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
- /// </summary>
- public string MediaSourceId { get; set; }
-
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Allows an override of supported number of audio channels
- /// Example: DeviceProfile supports five channel, but user only has stereo speakers
- /// </summary>
- public int? MaxAudioChannels { get; set; }
-
- /// <summary>
- /// The application's configured quality setting
- /// </summary>
- public long? MaxBitrate { get; set; }
-
- /// <summary>
- /// Gets or sets the context.
- /// </summary>
- /// <value>The context.</value>
- public EncodingContext Context { get; set; }
-
- /// <summary>
- /// Gets or sets the audio transcoding bitrate.
- /// </summary>
- /// <value>The audio transcoding bitrate.</value>
- public int? AudioTranscodingBitrate { get; set; }
-
- /// <summary>
- /// Gets the maximum bitrate.
- /// </summary>
- /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- public long? GetMaxBitrate(bool isAudio)
- {
- if (MaxBitrate.HasValue)
- {
- return MaxBitrate;
- }
-
- if (Profile != null)
- {
- if (Context == EncodingContext.Static)
- {
- if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
- {
- return Profile.MaxStaticMusicBitrate;
- }
- return Profile.MaxStaticBitrate;
- }
-
- return Profile.MaxStreamingBitrate;
- }
-
- return null;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs
deleted file mode 100644
index 6d143962d..000000000
--- a/MediaBrowser.Model/Dlna/CodecProfile.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class CodecProfile
- {
- [XmlAttribute("type")]
- public CodecType Type { get; set; }
-
- public ProfileCondition[] Conditions { get; set; }
-
- public ProfileCondition[] ApplyConditions { get; set; }
-
- [XmlAttribute("codec")]
- public string Codec { get; set; }
-
- [XmlAttribute("container")]
- public string Container { get; set; }
-
- public CodecProfile()
- {
- Conditions = new ProfileCondition[] {};
- ApplyConditions = new ProfileCondition[] { };
- }
-
- public string[] GetCodecs()
- {
- return ContainerProfile.SplitValue(Codec);
- }
-
- private bool ContainsContainer(string container)
- {
- return ContainerProfile.ContainsContainer(Container, container);
- }
-
- public bool ContainsAnyCodec(string codec, string container)
- {
- return ContainsAnyCodec(ContainerProfile.SplitValue(codec), container);
- }
-
- public bool ContainsAnyCodec(string[] codec, string container)
- {
- if (!ContainsContainer(container))
- {
- return false;
- }
-
- var codecs = GetCodecs();
- if (codecs.Length == 0)
- {
- return true;
- }
-
- foreach (var val in codec)
- {
- if (ListHelper.ContainsIgnoreCase(codecs, val))
- {
- return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/CodecType.cs b/MediaBrowser.Model/Dlna/CodecType.cs
deleted file mode 100644
index 415cae7ac..000000000
--- a/MediaBrowser.Model/Dlna/CodecType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum CodecType
- {
- Video = 0,
- VideoAudio = 1,
- Audio = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
deleted file mode 100644
index bd3dc6fd2..000000000
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ /dev/null
@@ -1,284 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ConditionProcessor
- {
- public bool IsVideoConditionSatisfied(ProfileCondition condition,
- int? width,
- int? height,
- int? videoBitDepth,
- int? videoBitrate,
- string videoProfile,
- double? videoLevel,
- float? videoFramerate,
- int? packetLength,
- TransportStreamTimestamp? timestamp,
- bool? isAnamorphic,
- bool? isInterlaced,
- int? refFrames,
- int? numVideoStreams,
- int? numAudioStreams,
- string videoCodecTag,
- bool? isAvc )
- {
- switch (condition.Property)
- {
- case ProfileConditionValue.IsInterlaced:
- return IsConditionSatisfied(condition, isInterlaced);
- case ProfileConditionValue.IsAnamorphic:
- return IsConditionSatisfied(condition, isAnamorphic);
- case ProfileConditionValue.IsAvc:
- return IsConditionSatisfied(condition, isAvc);
- case ProfileConditionValue.VideoFramerate:
- return IsConditionSatisfied(condition, videoFramerate);
- case ProfileConditionValue.VideoLevel:
- return IsConditionSatisfied(condition, videoLevel);
- case ProfileConditionValue.VideoProfile:
- return IsConditionSatisfied(condition, videoProfile);
- case ProfileConditionValue.VideoCodecTag:
- return IsConditionSatisfied(condition, videoCodecTag);
- case ProfileConditionValue.PacketLength:
- return IsConditionSatisfied(condition, packetLength);
- case ProfileConditionValue.VideoBitDepth:
- return IsConditionSatisfied(condition, videoBitDepth);
- case ProfileConditionValue.VideoBitrate:
- return IsConditionSatisfied(condition, videoBitrate);
- case ProfileConditionValue.Height:
- return IsConditionSatisfied(condition, height);
- case ProfileConditionValue.Width:
- return IsConditionSatisfied(condition, width);
- case ProfileConditionValue.RefFrames:
- return IsConditionSatisfied(condition, refFrames);
- case ProfileConditionValue.NumAudioStreams:
- return IsConditionSatisfied(condition, numAudioStreams);
- case ProfileConditionValue.NumVideoStreams:
- return IsConditionSatisfied(condition, numVideoStreams);
- case ProfileConditionValue.VideoTimestamp:
- return IsConditionSatisfied(condition, timestamp);
- default:
- return true;
- }
- }
-
- public bool IsImageConditionSatisfied(ProfileCondition condition, int? width, int? height)
- {
- switch (condition.Property)
- {
- case ProfileConditionValue.Height:
- return IsConditionSatisfied(condition, height);
- case ProfileConditionValue.Width:
- return IsConditionSatisfied(condition, width);
- default:
- throw new ArgumentException("Unexpected condition on image file: " + condition.Property);
- }
- }
-
- public bool IsAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
- {
- switch (condition.Property)
- {
- case ProfileConditionValue.AudioBitrate:
- return IsConditionSatisfied(condition, audioBitrate);
- case ProfileConditionValue.AudioChannels:
- return IsConditionSatisfied(condition, audioChannels);
- case ProfileConditionValue.AudioSampleRate:
- return IsConditionSatisfied(condition, audioSampleRate);
- case ProfileConditionValue.AudioBitDepth:
- return IsConditionSatisfied(condition, audioBitDepth);
- default:
- throw new ArgumentException("Unexpected condition on audio file: " + condition.Property);
- }
- }
-
- public bool IsVideoAudioConditionSatisfied(ProfileCondition condition,
- int? audioChannels,
- int? audioBitrate,
- int? audioSampleRate,
- int? audioBitDepth,
- string audioProfile,
- bool? isSecondaryTrack)
- {
- switch (condition.Property)
- {
- case ProfileConditionValue.AudioProfile:
- return IsConditionSatisfied(condition, audioProfile);
- case ProfileConditionValue.AudioBitrate:
- return IsConditionSatisfied(condition, audioBitrate);
- case ProfileConditionValue.AudioChannels:
- return IsConditionSatisfied(condition, audioChannels);
- case ProfileConditionValue.IsSecondaryAudio:
- return IsConditionSatisfied(condition, isSecondaryTrack);
- case ProfileConditionValue.AudioSampleRate:
- return IsConditionSatisfied(condition, audioSampleRate);
- case ProfileConditionValue.AudioBitDepth:
- return IsConditionSatisfied(condition, audioBitDepth);
- default:
- throw new ArgumentException("Unexpected condition on audio file: " + condition.Property);
- }
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, int? currentValue)
- {
- if (!currentValue.HasValue)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- int expected;
- if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
- {
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- case ProfileConditionType.EqualsAny:
- return currentValue.Value.Equals(expected);
- case ProfileConditionType.GreaterThanEqual:
- return currentValue.Value >= expected;
- case ProfileConditionType.LessThanEqual:
- return currentValue.Value <= expected;
- case ProfileConditionType.NotEquals:
- return !currentValue.Value.Equals(expected);
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- return false;
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, string currentValue)
- {
- if (string.IsNullOrEmpty(currentValue))
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- string expected = condition.Value;
-
- switch (condition.Condition)
- {
- case ProfileConditionType.EqualsAny:
- {
- return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
- }
- case ProfileConditionType.Equals:
- return StringHelper.EqualsIgnoreCase(currentValue, expected);
- case ProfileConditionType.NotEquals:
- return !StringHelper.EqualsIgnoreCase(currentValue, expected);
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, bool? currentValue)
- {
- if (!currentValue.HasValue)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- bool expected;
- if (bool.TryParse(condition.Value, out expected))
- {
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- return currentValue.Value == expected;
- case ProfileConditionType.NotEquals:
- return currentValue.Value != expected;
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- return false;
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, float? currentValue)
- {
- if (!currentValue.HasValue)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- float expected;
- if (float.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
- {
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- return currentValue.Value.Equals(expected);
- case ProfileConditionType.GreaterThanEqual:
- return currentValue.Value >= expected;
- case ProfileConditionType.LessThanEqual:
- return currentValue.Value <= expected;
- case ProfileConditionType.NotEquals:
- return !currentValue.Value.Equals(expected);
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- return false;
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
- {
- if (!currentValue.HasValue)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- double expected;
- if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
- {
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- return currentValue.Value.Equals(expected);
- case ProfileConditionType.GreaterThanEqual:
- return currentValue.Value >= expected;
- case ProfileConditionType.LessThanEqual:
- return currentValue.Value <= expected;
- case ProfileConditionType.NotEquals:
- return !currentValue.Value.Equals(expected);
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- return false;
- }
-
- private bool IsConditionSatisfied(ProfileCondition condition, TransportStreamTimestamp? timestamp)
- {
- if (!timestamp.HasValue)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- TransportStreamTimestamp expected = (TransportStreamTimestamp)Enum.Parse(typeof(TransportStreamTimestamp), condition.Value, true);
-
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- return timestamp == expected;
- case ProfileConditionType.NotEquals:
- return timestamp != expected;
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
deleted file mode 100644
index 23bbf0193..000000000
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ContainerProfile
- {
- [XmlAttribute("type")]
- public DlnaProfileType Type { get; set; }
- public ProfileCondition[] Conditions { get; set; }
-
- [XmlAttribute("container")]
- public string Container { get; set; }
-
- public ContainerProfile()
- {
- Conditions = new ProfileCondition[] { };
- }
-
- public string[] GetContainers()
- {
- return SplitValue(Container);
- }
-
- private static readonly string[] EmptyStringArray = new string[] { };
-
- public static string[] SplitValue(string value)
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- return EmptyStringArray;
- }
-
- return value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public bool ContainsContainer(string container)
- {
- var containers = GetContainers();
-
- return ContainsContainer(containers, container);
- }
-
- public static bool ContainsContainer(string profileContainers, string inputContainer)
- {
- return ContainsContainer(SplitValue(profileContainers), inputContainer);
- }
-
- public static bool ContainsContainer(string[] profileContainers, string inputContainer)
- {
- if (profileContainers.Length == 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/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
deleted file mode 100644
index 8a9dc1dd1..000000000
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ContentFeatureBuilder
- {
- private readonly DeviceProfile _profile;
-
- public ContentFeatureBuilder(DeviceProfile profile)
- {
- _profile = profile;
- }
-
- public string BuildImageHeader(string container,
- int? width,
- int? height,
- bool isDirectStream,
- string orgPn = null)
- {
- string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetImageOrgOpValue();
-
- // 0 = native, 1 = transcoded
- var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
-
- DlnaFlags flagValue = DlnaFlags.BackgroundTransferMode |
- DlnaFlags.InteractiveTransferMode |
- DlnaFlags.DlnaV15;
-
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
- DlnaMaps.FlagsToString(flagValue));
-
- ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
- width,
- height);
-
- if (string.IsNullOrEmpty(orgPn))
- {
- orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
- }
-
- if (string.IsNullOrEmpty(orgPn))
- {
- orgPn = GetImageOrgPnValue(container, width, height);
- }
-
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
-
- return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
- }
-
- public string BuildAudioHeader(string container,
- string audioCodec,
- int? audioBitrate,
- int? audioSampleRate,
- int? audioChannels,
- int? audioBitDepth,
- bool isDirectStream,
- long? runtimeTicks,
- TranscodeSeekInfo transcodeSeekInfo)
- {
- // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
- string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
-
- // 0 = native, 1 = transcoded
- string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
-
- DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.InteractiveTransferMode |
- DlnaFlags.DlnaV15;
-
- //if (isDirectStream)
- //{
- // flagValue = flagValue | DlnaFlags.ByteBasedSeek;
- //}
- //else if (runtimeTicks.HasValue)
- //{
- // flagValue = flagValue | DlnaFlags.TimeBasedSeek;
- //}
-
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
- DlnaMaps.FlagsToString(flagValue));
-
- ResponseProfile mediaProfile = _profile.GetAudioMediaProfile(container,
- audioCodec,
- audioChannels,
- audioBitrate,
- audioSampleRate,
- audioBitDepth);
-
- string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
-
- if (string.IsNullOrEmpty(orgPn))
- {
- orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels);
- }
-
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
-
- return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
- }
-
- public List<string> BuildVideoHeader(string container,
- string videoCodec,
- string audioCodec,
- int? width,
- int? height,
- int? bitDepth,
- int? videoBitrate,
- TransportStreamTimestamp timestamp,
- bool isDirectStream,
- long? runtimeTicks,
- string videoProfile,
- double? videoLevel,
- float? videoFramerate,
- int? packetLength,
- TranscodeSeekInfo transcodeSeekInfo,
- bool? isAnamorphic,
- bool? isInterlaced,
- int? refFrames,
- int? numVideoStreams,
- int? numAudioStreams,
- string videoCodecTag,
- bool? isAvc)
- {
- // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
- string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
-
- // 0 = native, 1 = transcoded
- string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
-
- DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.InteractiveTransferMode |
- DlnaFlags.DlnaV15;
-
- //if (isDirectStream)
- //{
- // flagValue = flagValue | DlnaFlags.ByteBasedSeek;
- //}
- //else if (runtimeTicks.HasValue)
- //{
- // flagValue = flagValue | DlnaFlags.TimeBasedSeek;
- //}
-
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
- DlnaMaps.FlagsToString(flagValue));
-
- ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,
- audioCodec,
- videoCodec,
- width,
- height,
- bitDepth,
- videoBitrate,
- videoProfile,
- videoLevel,
- videoFramerate,
- packetLength,
- timestamp,
- isAnamorphic,
- isInterlaced,
- refFrames,
- numVideoStreams,
- numAudioStreams,
- videoCodecTag,
- isAvc);
-
- List<string> orgPnValues = new List<string>();
-
- if (mediaProfile != null && !string.IsNullOrEmpty(mediaProfile.OrgPn))
- {
- orgPnValues.AddRange(mediaProfile.OrgPn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
- }
- else
- {
- foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp))
- {
- orgPnValues.Add(s);
- break;
- }
- }
-
- List<string> contentFeatureList = new List<string>();
-
- foreach (string orgPn in orgPnValues)
- {
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
-
- var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
-
- contentFeatureList.Add(value);
- }
-
- if (orgPnValues.Count == 0)
- {
- string contentFeatures = string.Empty;
-
- var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
-
- contentFeatureList.Add(value);
- }
-
- return contentFeatureList;
- }
-
- private string GetImageOrgPnValue(string container, int? width, int? height)
- {
- MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveImageFormat(container,
- width,
- height);
-
- return format.HasValue ? format.Value.ToString() : null;
- }
-
- private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
- {
- MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveAudioFormat(container,
- audioBitrate,
- audioSampleRate,
- audioChannels);
-
- return format.HasValue ? format.Value.ToString() : null;
- }
-
- private List<string> GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
- {
- List<string> list = new List<string>();
- foreach (MediaFormatProfile i in new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp))
- list.Add(i.ToString());
- return list;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs
deleted file mode 100644
index 97f4409da..000000000
--- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public class DeviceIdentification
- {
- /// <summary>
- /// Gets or sets the name of the friendly.
- /// </summary>
- /// <value>The name of the friendly.</value>
- public string FriendlyName { get; set; }
- /// <summary>
- /// Gets or sets the model number.
- /// </summary>
- /// <value>The model number.</value>
- public string ModelNumber { get; set; }
- /// <summary>
- /// Gets or sets the serial number.
- /// </summary>
- /// <value>The serial number.</value>
- public string SerialNumber { get; set; }
- /// <summary>
- /// Gets or sets the name of the model.
- /// </summary>
- /// <value>The name of the model.</value>
- public string ModelName { get; set; }
- /// <summary>
- /// Gets or sets the model description.
- /// </summary>
- /// <value>The model description.</value>
- public string ModelDescription { get; set; }
- /// <summary>
- /// Gets or sets the device description.
- /// </summary>
- /// <value>The device description.</value>
- public string DeviceDescription { get; set; }
- /// <summary>
- /// Gets or sets the model URL.
- /// </summary>
- /// <value>The model URL.</value>
- public string ModelUrl { get; set; }
- /// <summary>
- /// Gets or sets the manufacturer.
- /// </summary>
- /// <value>The manufacturer.</value>
- public string Manufacturer { get; set; }
- /// <summary>
- /// Gets or sets the manufacturer URL.
- /// </summary>
- /// <value>The manufacturer URL.</value>
- public string ManufacturerUrl { get; set; }
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public HttpHeaderInfo[] Headers { get; set; }
-
- public DeviceIdentification()
- {
- Headers = new HttpHeaderInfo[] {};
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
deleted file mode 100644
index fc976b605..000000000
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ /dev/null
@@ -1,334 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-
-namespace MediaBrowser.Model.Dlna
-{
- [XmlRoot("Profile")]
- public class DeviceProfile
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- [XmlIgnore]
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the identification.
- /// </summary>
- /// <value>The identification.</value>
- public MediaBrowser.Model.Dlna.DeviceIdentification Identification { get; set; }
-
- public string FriendlyName { get; set; }
- public string Manufacturer { get; set; }
- public string ManufacturerUrl { get; set; }
- public string ModelName { get; set; }
- public string ModelDescription { get; set; }
- public string ModelNumber { get; set; }
- public string ModelUrl { get; set; }
- public string SerialNumber { get; set; }
-
- public bool EnableAlbumArtInDidl { get; set; }
- public bool EnableSingleAlbumArtLimit { get; set; }
- public bool EnableSingleSubtitleLimit { get; set; }
-
- public string SupportedMediaTypes { get; set; }
-
- public string UserId { get; set; }
-
- public string AlbumArtPn { get; set; }
-
- public int MaxAlbumArtWidth { get; set; }
- public int MaxAlbumArtHeight { get; set; }
-
- public int? MaxIconWidth { get; set; }
- public int? MaxIconHeight { get; set; }
-
- public long? MaxStreamingBitrate { get; set; }
- public long? MaxStaticBitrate { get; set; }
-
- public int? MusicStreamingTranscodingBitrate { get; set; }
- public int? MaxStaticMusicBitrate { get; set; }
-
- /// <summary>
- /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
- /// </summary>
- public string XDlnaDoc { get; set; }
- /// <summary>
- /// Controls the content of the X_DLNACAP element in the urn:schemas-dlna-org:device-1-0 namespace.
- /// </summary>
- public string XDlnaCap { get; set; }
- /// <summary>
- /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.
- /// </summary>
- public string SonyAggregationFlags { get; set; }
-
- public string ProtocolInfo { get; set; }
-
- public int TimelineOffsetSeconds { get; set; }
- public bool RequiresPlainVideoItems { get; set; }
- public bool RequiresPlainFolders { get; set; }
-
- public bool EnableMSMediaReceiverRegistrar { get; set; }
- public bool IgnoreTranscodeByteRangeRequests { get; set; }
-
- public XmlAttribute[] XmlRootAttributes { get; set; }
-
- /// <summary>
- /// Gets or sets the direct play profiles.
- /// </summary>
- /// <value>The direct play profiles.</value>
- public DirectPlayProfile[] DirectPlayProfiles { get; set; }
-
- /// <summary>
- /// Gets or sets the transcoding profiles.
- /// </summary>
- /// <value>The transcoding profiles.</value>
- public TranscodingProfile[] TranscodingProfiles { get; set; }
-
- public ContainerProfile[] ContainerProfiles { get; set; }
-
- public CodecProfile[] CodecProfiles { get; set; }
- public ResponseProfile[] ResponseProfiles { get; set; }
-
- public SubtitleProfile[] SubtitleProfiles { get; set; }
-
- public DeviceProfile()
- {
- DirectPlayProfiles = new DirectPlayProfile[] { };
- TranscodingProfiles = new TranscodingProfile[] { };
- ResponseProfiles = new ResponseProfile[] { };
- CodecProfiles = new CodecProfile[] { };
- ContainerProfiles = new ContainerProfile[] { };
- SubtitleProfiles = new SubtitleProfile[] { };
-
- XmlRootAttributes = new XmlAttribute[] { };
-
- SupportedMediaTypes = "Audio,Photo,Video";
- MaxStreamingBitrate = 8000000;
- MaxStaticBitrate = 8000000;
- MusicStreamingTranscodingBitrate = 128000;
- }
-
- public string[] GetSupportedMediaTypes()
- {
- return ContainerProfile.SplitValue(SupportedMediaTypes);
- }
-
- public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec)
- {
- container = StringHelper.TrimStart(container ?? string.Empty, '.');
-
- foreach (var i in TranscodingProfiles)
- {
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
- {
- continue;
- }
-
- if (!StringHelper.EqualsIgnoreCase(container, i.Container))
- {
- continue;
- }
-
- if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
- {
- continue;
- }
-
- return i;
- }
- return null;
- }
-
- public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec)
- {
- container = StringHelper.TrimStart(container ?? string.Empty, '.');
-
- foreach (var i in TranscodingProfiles)
- {
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
- {
- continue;
- }
-
- if (!StringHelper.EqualsIgnoreCase(container, i.Container))
- {
- continue;
- }
-
- if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
- {
- continue;
- }
-
- if (!StringHelper.EqualsIgnoreCase(videoCodec, i.VideoCodec ?? string.Empty))
- {
- continue;
- }
-
- return i;
- }
- return null;
- }
-
- public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
- {
- foreach (var i in ResponseProfiles)
- {
- if (i.Type != DlnaProfileType.Audio)
- {
- continue;
- }
-
- if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
- {
- continue;
- }
-
- var audioCodecs = i.GetAudioCodecs();
- if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
- {
- continue;
- }
-
- var conditionProcessor = new ConditionProcessor();
-
- var anyOff = false;
- foreach (ProfileCondition c in i.Conditions)
- {
- if (!conditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth))
- {
- anyOff = true;
- break;
- }
- }
-
- if (anyOff)
- {
- continue;
- }
-
- return i;
- }
- return null;
- }
-
- private ProfileCondition GetModelProfileCondition(ProfileCondition c)
- {
- return new ProfileCondition
- {
- Condition = c.Condition,
- IsRequired = c.IsRequired,
- Property = c.Property,
- Value = c.Value
- };
- }
-
- public ResponseProfile GetImageMediaProfile(string container, int? width, int? height)
- {
- foreach (var i in ResponseProfiles)
- {
- if (i.Type != DlnaProfileType.Photo)
- {
- continue;
- }
-
- if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
- {
- continue;
- }
-
- var conditionProcessor = new ConditionProcessor();
-
- var anyOff = false;
- foreach (ProfileCondition c in i.Conditions)
- {
- if (!conditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height))
- {
- anyOff = true;
- break;
- }
- }
-
- if (anyOff)
- {
- continue;
- }
-
- return i;
- }
- return null;
- }
-
- public ResponseProfile GetVideoMediaProfile(string container,
- string audioCodec,
- string videoCodec,
- int? width,
- int? height,
- int? bitDepth,
- int? videoBitrate,
- string videoProfile,
- double? videoLevel,
- float? videoFramerate,
- int? packetLength,
- TransportStreamTimestamp timestamp,
- bool? isAnamorphic,
- bool? isInterlaced,
- int? refFrames,
- int? numVideoStreams,
- int? numAudioStreams,
- string videoCodecTag,
- bool? isAvc)
- {
- foreach (var i in ResponseProfiles)
- {
- if (i.Type != DlnaProfileType.Video)
- {
- continue;
- }
-
- if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
- {
- continue;
- }
-
- var audioCodecs = i.GetAudioCodecs();
- if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
- {
- continue;
- }
-
- var videoCodecs = i.GetVideoCodecs();
- if (videoCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec ?? string.Empty))
- {
- continue;
- }
-
- var conditionProcessor = new ConditionProcessor();
-
- var anyOff = false;
- foreach (ProfileCondition c in i.Conditions)
- {
- if (!conditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- anyOff = true;
- break;
- }
- }
-
- if (anyOff)
- {
- continue;
- }
-
- return i;
- }
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
deleted file mode 100644
index b2afdf292..000000000
--- a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-namespace MediaBrowser.Model.Dlna
-{
- public class DeviceProfileInfo
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public DeviceProfileType Type { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfileType.cs b/MediaBrowser.Model/Dlna/DeviceProfileType.cs
deleted file mode 100644
index f881a4539..000000000
--- a/MediaBrowser.Model/Dlna/DeviceProfileType.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum DeviceProfileType
- {
- System = 0,
- User = 1
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
deleted file mode 100644
index 7430c449f..000000000
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class DirectPlayProfile
- {
- [XmlAttribute("container")]
- public string Container { get; set; }
-
- [XmlAttribute("audioCodec")]
- public string AudioCodec { get; set; }
-
- [XmlAttribute("videoCodec")]
- public string VideoCodec { get; set; }
-
- [XmlAttribute("type")]
- public DlnaProfileType Type { get; set; }
-
- public bool SupportsContainer(string container)
- {
- return ContainerProfile.ContainsContainer(Container, container);
- }
-
- public string[] GetAudioCodecs()
- {
- return ContainerProfile.SplitValue(AudioCodec);
- }
-
- public string[] GetVideoCodecs()
- {
- return ContainerProfile.SplitValue(VideoCodec);
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DlnaFlags.cs b/MediaBrowser.Model/Dlna/DlnaFlags.cs
deleted file mode 100644
index b981e8455..000000000
--- a/MediaBrowser.Model/Dlna/DlnaFlags.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Dlna
-{
- [Flags]
- public enum DlnaFlags : ulong
- {
- /*! <i>Background</i> transfer mode.
- For use with upload and download transfers to and from the server.
- The primary difference between \ref DH_TransferMode_Interactive and
- \ref DH_TransferMode_Bulk is that the latter assumes that the user
- is not relying on the transfer for immediately rendering the content
- and there are no issues with causing a buffer overflow if the
- receiver uses TCP flow control to reduce total throughput.
- */
- BackgroundTransferMode = 1 << 22,
-
- ByteBasedSeek = 1 << 29,
- ConnectionStall = 1 << 21,
-
- DlnaV15 = 1 << 20,
-
- /*! <i>Interactive</i> transfer mode.
- For best effort transfer of images and non-real-time transfers.
- URIs with image content usually support \ref DH_TransferMode_Bulk too.
- The primary difference between \ref DH_TransferMode_Interactive and
- \ref DH_TransferMode_Bulk is that the former assumes that the
- transfer is intended for immediate rendering.
- */
- InteractiveTransferMode = 1 << 23,
-
- PlayContainer = 1 << 28,
- RtspPause = 1 << 25,
- S0Increase = 1 << 27,
- SenderPaced = 1L << 31,
- SnIncrease = 1 << 26,
-
- /*! <i>Streaming</i> transfer mode.
- The server transmits at a throughput sufficient for real-time playback of
- audio or video. URIs with audio or video often support the
- \ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes.
- The most well-known exception to this general claim is for live streams.
- */
- StreamingTransferMode = 1 << 24,
-
- TimeBasedSeek = 1 << 30
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs
deleted file mode 100644
index 9a84eb195..000000000
--- a/MediaBrowser.Model/Dlna/DlnaMaps.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public class DlnaMaps
- {
- public static readonly string DefaultStreaming =
- FlagsToString(DlnaFlags.StreamingTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.ConnectionStall |
- DlnaFlags.ByteBasedSeek |
- DlnaFlags.DlnaV15);
-
- public static readonly string DefaultInteractive =
- FlagsToString(DlnaFlags.InteractiveTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.ConnectionStall |
- DlnaFlags.ByteBasedSeek |
- DlnaFlags.DlnaV15);
-
- public static string FlagsToString(DlnaFlags flags)
- {
- return string.Format("{0:X8}{1:D24}", (ulong)flags, 0);
- }
-
- public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo)
- {
- if (hasKnownRuntime)
- {
- string orgOp = string.Empty;
-
- // Time-based seeking currently only possible when transcoding
- orgOp += isDirectStream ? "0" : "1";
-
- // Byte-based seeking only possible when not transcoding
- orgOp += isDirectStream || profileTranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
-
- return orgOp;
- }
-
- // No seeking is available if we don't know the content runtime
- return "00";
- }
-
- public static string GetImageOrgOpValue()
- {
- string orgOp = string.Empty;
-
- // Time-based seeking currently only possible when transcoding
- orgOp += "0";
-
- // Byte-based seeking only possible when not transcoding
- orgOp += "0";
-
- return orgOp;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/DlnaProfileType.cs b/MediaBrowser.Model/Dlna/DlnaProfileType.cs
deleted file mode 100644
index 1bad14081..000000000
--- a/MediaBrowser.Model/Dlna/DlnaProfileType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum DlnaProfileType
- {
- Audio = 0,
- Video = 1,
- Photo = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/EncodingContext.cs b/MediaBrowser.Model/Dlna/EncodingContext.cs
deleted file mode 100644
index f83d8ddc8..000000000
--- a/MediaBrowser.Model/Dlna/EncodingContext.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum EncodingContext
- {
- Streaming = 0,
- Static = 1
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/HeaderMatchType.cs b/MediaBrowser.Model/Dlna/HeaderMatchType.cs
deleted file mode 100644
index 7a0d5c24f..000000000
--- a/MediaBrowser.Model/Dlna/HeaderMatchType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum HeaderMatchType
- {
- Equals = 0,
- Regex = 1,
- Substring = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
deleted file mode 100644
index b4fa4e0af..000000000
--- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class HttpHeaderInfo
- {
- [XmlAttribute("name")]
- public string Name { get; set; }
-
- [XmlAttribute("value")]
- public string Value { get; set; }
-
- [XmlAttribute("match")]
- public HeaderMatchType Match { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
deleted file mode 100644
index 70191ff23..000000000
--- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using MediaBrowser.Model.Events;
-
-namespace MediaBrowser.Model.Dlna
-{
- public interface IDeviceDiscovery
- {
- event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
- event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
- }
-}
diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
deleted file mode 100644
index 14723bd27..000000000
--- a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public interface ITranscoderSupport
- {
- bool CanEncodeToAudioCodec(string codec);
- bool CanEncodeToSubtitleCodec(string codec);
- bool CanExtractSubtitles(string codec);
- }
-
- public class FullTranscoderSupport : ITranscoderSupport
- {
- public bool CanEncodeToAudioCodec(string codec)
- {
- return true;
- }
- public bool CanEncodeToSubtitleCodec(string codec)
- {
- return true;
- }
- public bool CanExtractSubtitles(string codec)
- {
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfile.cs b/MediaBrowser.Model/Dlna/MediaFormatProfile.cs
deleted file mode 100644
index f3d04335f..000000000
--- a/MediaBrowser.Model/Dlna/MediaFormatProfile.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-
-namespace MediaBrowser.Model.Dlna
-{
- public enum MediaFormatProfile
- {
- MP3,
- WMA_BASE,
- WMA_FULL,
- LPCM16_44_MONO,
- LPCM16_44_STEREO,
- LPCM16_48_MONO,
- LPCM16_48_STEREO,
- AAC_ISO,
- AAC_ISO_320,
- AAC_ADTS,
- AAC_ADTS_320,
- FLAC,
- OGG,
-
- JPEG_SM,
- JPEG_MED,
- JPEG_LRG,
- JPEG_TN,
- PNG_LRG,
- PNG_TN,
- GIF_LRG,
- RAW,
-
- MPEG1,
- MPEG_PS_PAL,
- MPEG_PS_NTSC,
- MPEG_TS_SD_EU,
- MPEG_TS_SD_EU_ISO,
- MPEG_TS_SD_EU_T,
- MPEG_TS_SD_NA,
- MPEG_TS_SD_NA_ISO,
- MPEG_TS_SD_NA_T,
- MPEG_TS_SD_KO,
- MPEG_TS_SD_KO_ISO,
- MPEG_TS_SD_KO_T,
- MPEG_TS_JP_T,
- AVI,
- MATROSKA,
- FLV,
- DVR_MS,
- WTV,
- OGV,
- AVC_MP4_MP_SD_AAC_MULT5,
- AVC_MP4_MP_SD_MPEG1_L3,
- AVC_MP4_MP_SD_AC3,
- AVC_MP4_MP_HD_720p_AAC,
- AVC_MP4_MP_HD_1080i_AAC,
- AVC_MP4_HP_HD_AAC,
- AVC_TS_MP_HD_AAC_MULT5,
- AVC_TS_MP_HD_AAC_MULT5_T,
- AVC_TS_MP_HD_AAC_MULT5_ISO,
- AVC_TS_MP_HD_MPEG1_L3,
- AVC_TS_MP_HD_MPEG1_L3_T,
- AVC_TS_MP_HD_MPEG1_L3_ISO,
- AVC_TS_MP_HD_AC3,
- AVC_TS_MP_HD_AC3_T,
- AVC_TS_MP_HD_AC3_ISO,
- AVC_TS_HP_HD_MPEG1_L2_T,
- AVC_TS_HP_HD_MPEG1_L2_ISO,
- AVC_TS_MP_SD_AAC_MULT5,
- AVC_TS_MP_SD_AAC_MULT5_T,
- AVC_TS_MP_SD_AAC_MULT5_ISO,
- AVC_TS_MP_SD_MPEG1_L3,
- AVC_TS_MP_SD_MPEG1_L3_T,
- AVC_TS_MP_SD_MPEG1_L3_ISO,
- AVC_TS_HP_SD_MPEG1_L2_T,
- AVC_TS_HP_SD_MPEG1_L2_ISO,
- AVC_TS_MP_SD_AC3,
- AVC_TS_MP_SD_AC3_T,
- AVC_TS_MP_SD_AC3_ISO,
- AVC_TS_HD_DTS_T,
- AVC_TS_HD_DTS_ISO,
- WMVMED_BASE,
- WMVMED_FULL,
- WMVMED_PRO,
- WMVHIGH_FULL,
- WMVHIGH_PRO,
- VC1_ASF_AP_L1_WMA,
- VC1_ASF_AP_L2_WMA,
- VC1_ASF_AP_L3_WMA,
- VC1_TS_AP_L1_AC3_ISO,
- VC1_TS_AP_L2_AC3_ISO,
- VC1_TS_HD_DTS_ISO,
- VC1_TS_HD_DTS_T,
- MPEG4_P2_MP4_ASP_AAC,
- MPEG4_P2_MP4_SP_L6_AAC,
- MPEG4_P2_MP4_NDSD,
- MPEG4_P2_TS_ASP_AAC,
- MPEG4_P2_TS_ASP_AAC_T,
- MPEG4_P2_TS_ASP_AAC_ISO,
- MPEG4_P2_TS_ASP_MPEG1_L3,
- MPEG4_P2_TS_ASP_MPEG1_L3_T,
- MPEG4_P2_TS_ASP_MPEG1_L3_ISO,
- MPEG4_P2_TS_ASP_MPEG2_L2,
- MPEG4_P2_TS_ASP_MPEG2_L2_T,
- MPEG4_P2_TS_ASP_MPEG2_L2_ISO,
- MPEG4_P2_TS_ASP_AC3,
- MPEG4_P2_TS_ASP_AC3_T,
- MPEG4_P2_TS_ASP_AC3_ISO,
- AVC_TS_HD_50_LPCM_T,
- AVC_MP4_LPCM,
- MPEG4_P2_3GPP_SP_L0B_AAC,
- MPEG4_P2_3GPP_SP_L0B_AMR,
- AVC_3GPP_BL_QCIF15_AAC,
- MPEG4_H263_3GPP_P0_L10_AMR,
- MPEG4_H263_MP4_P0_L10_AAC
- }
-}
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
deleted file mode 100644
index 034e0fe6a..000000000
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ /dev/null
@@ -1,431 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class MediaFormatProfileResolver
- {
- public MediaFormatProfile[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
- {
- if (StringHelper.EqualsIgnoreCase(container, "asf"))
- {
- MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[]{};
- }
-
- if (StringHelper.EqualsIgnoreCase(container, "mp4"))
- {
- MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
- }
-
- if (StringHelper.EqualsIgnoreCase(container, "avi"))
- return new MediaFormatProfile[] { MediaFormatProfile.AVI };
-
- if (StringHelper.EqualsIgnoreCase(container, "mkv"))
- return new MediaFormatProfile[] { MediaFormatProfile.MATROSKA };
-
- if (StringHelper.EqualsIgnoreCase(container, "mpeg2ps") ||
- StringHelper.EqualsIgnoreCase(container, "ts"))
-
- return new MediaFormatProfile[] { MediaFormatProfile.MPEG_PS_NTSC, MediaFormatProfile.MPEG_PS_PAL };
-
- if (StringHelper.EqualsIgnoreCase(container, "mpeg1video"))
- return new MediaFormatProfile[] { MediaFormatProfile.MPEG1 };
-
- if (StringHelper.EqualsIgnoreCase(container, "mpeg2ts") ||
- StringHelper.EqualsIgnoreCase(container, "mpegts") ||
- StringHelper.EqualsIgnoreCase(container, "m2ts"))
- {
-
- return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType);
- }
-
- if (StringHelper.EqualsIgnoreCase(container, "flv"))
- return new MediaFormatProfile[] { MediaFormatProfile.FLV };
-
- if (StringHelper.EqualsIgnoreCase(container, "wtv"))
- return new MediaFormatProfile[] { MediaFormatProfile.WTV };
-
- if (StringHelper.EqualsIgnoreCase(container, "3gp"))
- {
- MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
- }
-
- if (StringHelper.EqualsIgnoreCase(container, "ogv") || StringHelper.EqualsIgnoreCase(container, "ogg"))
- return new MediaFormatProfile[] { MediaFormatProfile.OGV };
-
- return new MediaFormatProfile[] { };
- }
-
- private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
- {
- string suffix = "";
-
- switch (timestampType)
- {
- case TransportStreamTimestamp.None:
- suffix = "_ISO";
- break;
- case TransportStreamTimestamp.Valid:
- suffix = "_T";
- break;
- }
-
- string resolution = "S";
- if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576))
- {
- resolution = "H";
- }
-
- if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
- {
- List<MediaFormatProfile> list = new List<MediaFormatProfile>();
-
- list.Add(ValueOf("MPEG_TS_SD_NA" + suffix));
- list.Add(ValueOf("MPEG_TS_SD_EU" + suffix));
- list.Add(ValueOf("MPEG_TS_SD_KO" + suffix));
-
- if ((timestampType == TransportStreamTimestamp.Valid) && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- {
- list.Add(MediaFormatProfile.MPEG_TS_JP_T);
- }
- return list.ToArray(list.Count);
- }
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
- return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_50_LPCM_T };
-
- if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
- {
- if (timestampType == TransportStreamTimestamp.None)
- {
- return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_ISO };
- }
- return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T };
- }
-
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
- {
- if (timestampType == TransportStreamTimestamp.None)
- {
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
- }
-
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
- }
-
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
-
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
-
- if (string.IsNullOrEmpty(audioCodec) ||
- StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
- {
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
- {
- if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576))
- {
- return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO };
- }
- return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO };
- }
- if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
- {
- suffix = StringHelper.EqualsIgnoreCase(suffix, "_ISO") ? suffix : "_T";
-
- return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) };
- }
-
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
- }
-
- return new MediaFormatProfile[]{};
- }
-
- private MediaFormatProfile ValueOf(string value)
- {
- return (MediaFormatProfile)Enum.Parse(typeof(MediaFormatProfile), value, true);
- }
-
- private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height)
- {
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
- return MediaFormatProfile.AVC_MP4_LPCM;
- if (string.IsNullOrEmpty(audioCodec) ||
- StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
- {
- return MediaFormatProfile.AVC_MP4_MP_SD_AC3;
- }
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
- {
- return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3;
- }
- if (width.HasValue && height.HasValue)
- {
- if ((width.Value <= 720) && (height.Value <= 576))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5;
- }
- else if ((width.Value <= 1280) && (height.Value <= 720))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC;
- }
- else if ((width.Value <= 1920) && (height.Value <= 1080))
- {
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- {
- return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC;
- }
- }
- }
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
- StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
- {
- if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576)
- {
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC;
- if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3") || StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
- {
- return MediaFormatProfile.MPEG4_P2_MP4_NDSD;
- }
- }
- else if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- {
- return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC;
- }
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- {
- return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC;
- }
-
- return null;
- }
-
- private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec)
- {
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
- {
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
- return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC;
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
- StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
- {
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
- return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC;
- if (StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
- return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR;
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
- {
- return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR;
- }
-
- return null;
- }
-
- private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height)
- {
- if (StringHelper.EqualsIgnoreCase(videoCodec, "wmv") &&
- (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma") || StringHelper.EqualsIgnoreCase(videoCodec, "wmapro")))
- {
-
- if (width.HasValue && height.HasValue)
- {
- if ((width.Value <= 720) && (height.Value <= 576))
- {
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
- {
- return MediaFormatProfile.WMVMED_FULL;
- }
- return MediaFormatProfile.WMVMED_PRO;
- }
- }
-
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
- {
- return MediaFormatProfile.WMVHIGH_FULL;
- }
- return MediaFormatProfile.WMVHIGH_PRO;
- }
-
- if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
- {
- if (width.HasValue && height.HasValue)
- {
- if ((width.Value <= 720) && (height.Value <= 576))
- return MediaFormatProfile.VC1_ASF_AP_L1_WMA;
- if ((width.Value <= 1280) && (height.Value <= 720))
- return MediaFormatProfile.VC1_ASF_AP_L2_WMA;
- if ((width.Value <= 1920) && (height.Value <= 1080))
- return MediaFormatProfile.VC1_ASF_AP_L3_WMA;
- }
- }
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
- {
- return MediaFormatProfile.DVR_MS;
- }
-
- return null;
- }
-
- public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
- {
- if (StringHelper.EqualsIgnoreCase(container, "asf"))
- return ResolveAudioASFFormat(bitrate);
-
- if (StringHelper.EqualsIgnoreCase(container, "mp3"))
- return MediaFormatProfile.MP3;
-
- if (StringHelper.EqualsIgnoreCase(container, "lpcm"))
- return ResolveAudioLPCMFormat(frequency, channels);
-
- if (StringHelper.EqualsIgnoreCase(container, "mp4") ||
- StringHelper.EqualsIgnoreCase(container, "aac"))
- return ResolveAudioMP4Format(bitrate);
-
- if (StringHelper.EqualsIgnoreCase(container, "adts"))
- return ResolveAudioADTSFormat(bitrate);
-
- if (StringHelper.EqualsIgnoreCase(container, "flac"))
- return MediaFormatProfile.FLAC;
-
- if (StringHelper.EqualsIgnoreCase(container, "oga") ||
- StringHelper.EqualsIgnoreCase(container, "ogg"))
- return MediaFormatProfile.OGG;
-
- return null;
- }
-
- private MediaFormatProfile ResolveAudioASFFormat(int? bitrate)
- {
- if (bitrate.HasValue && bitrate.Value <= 193)
- {
- return MediaFormatProfile.WMA_BASE;
- }
- return MediaFormatProfile.WMA_FULL;
- }
-
- private MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels)
- {
- if (frequency.HasValue && channels.HasValue)
- {
- if (frequency.Value == 44100 && channels.Value == 1)
- {
- return MediaFormatProfile.LPCM16_44_MONO;
- }
- if (frequency.Value == 44100 && channels.Value == 2)
- {
- return MediaFormatProfile.LPCM16_44_STEREO;
- }
- if (frequency.Value == 48000 && channels.Value == 1)
- {
- return MediaFormatProfile.LPCM16_48_MONO;
- }
- if (frequency.Value == 48000 && channels.Value == 2)
- {
- return MediaFormatProfile.LPCM16_48_STEREO;
- }
-
- return null;
- }
-
- return MediaFormatProfile.LPCM16_48_STEREO;
- }
-
- private MediaFormatProfile ResolveAudioMP4Format(int? bitrate)
- {
- if (bitrate.HasValue && bitrate.Value <= 320)
- {
- return MediaFormatProfile.AAC_ISO_320;
- }
- return MediaFormatProfile.AAC_ISO;
- }
-
- private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate)
- {
- if (bitrate.HasValue && bitrate.Value <= 320)
- {
- return MediaFormatProfile.AAC_ADTS_320;
- }
- return MediaFormatProfile.AAC_ADTS;
- }
-
- public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height)
- {
- if (StringHelper.EqualsIgnoreCase(container, "jpeg") ||
- StringHelper.EqualsIgnoreCase(container, "jpg"))
- return ResolveImageJPGFormat(width, height);
-
- if (StringHelper.EqualsIgnoreCase(container, "png"))
- return ResolveImagePNGFormat(width, height);
-
- if (StringHelper.EqualsIgnoreCase(container, "gif"))
- return MediaFormatProfile.GIF_LRG;
-
- if (StringHelper.EqualsIgnoreCase(container, "raw"))
- return MediaFormatProfile.RAW;
-
- return null;
- }
-
- private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height)
- {
- if (width.HasValue && height.HasValue)
- {
- if ((width.Value <= 160) && (height.Value <= 160))
- return MediaFormatProfile.JPEG_TN;
-
- if ((width.Value <= 640) && (height.Value <= 480))
- return MediaFormatProfile.JPEG_SM;
-
- if ((width.Value <= 1024) && (height.Value <= 768))
- {
- return MediaFormatProfile.JPEG_MED;
- }
-
- return MediaFormatProfile.JPEG_LRG;
- }
-
- return MediaFormatProfile.JPEG_SM;
- }
-
- private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height)
- {
- if (width.HasValue && height.HasValue)
- {
- if ((width.Value <= 160) && (height.Value <= 160))
- return MediaFormatProfile.PNG_TN;
- }
-
- return MediaFormatProfile.PNG_LRG;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
deleted file mode 100644
index 4ed412985..000000000
--- a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Dlna
-{
- public enum PlaybackErrorCode
- {
- NotAllowed = 0,
- NoCompatibleStream = 1,
- RateLimitExceeded = 2
- }
-}
diff --git a/MediaBrowser.Model/Dlna/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs
deleted file mode 100644
index 9234a2713..000000000
--- a/MediaBrowser.Model/Dlna/ProfileCondition.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.Xml.Serialization;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ProfileCondition
- {
- [XmlAttribute("condition")]
- public ProfileConditionType Condition { get; set; }
-
- [XmlAttribute("property")]
- public ProfileConditionValue Property { get; set; }
-
- [XmlAttribute("value")]
- public string Value { get; set; }
-
- [XmlAttribute("isRequired")]
- public bool IsRequired { get; set; }
-
- public ProfileCondition()
- {
- IsRequired = true;
- }
-
- public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value)
- : this(condition, property, value, false)
- {
-
- }
-
- public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value, bool isRequired)
- {
- Condition = condition;
- Property = property;
- Value = value;
- IsRequired = isRequired;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionType.cs b/MediaBrowser.Model/Dlna/ProfileConditionType.cs
deleted file mode 100644
index b0a94c5b3..000000000
--- a/MediaBrowser.Model/Dlna/ProfileConditionType.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum ProfileConditionType
- {
- Equals = 0,
- NotEquals = 1,
- LessThanEqual = 2,
- GreaterThanEqual = 3,
- EqualsAny = 4
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
deleted file mode 100644
index a96e9ac36..000000000
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum ProfileConditionValue
- {
- AudioChannels = 0,
- AudioBitrate = 1,
- AudioProfile = 2,
- Width = 3,
- Height = 4,
- Has64BitOffsets = 5,
- PacketLength = 6,
- VideoBitDepth = 7,
- VideoBitrate = 8,
- VideoFramerate = 9,
- VideoLevel = 10,
- VideoProfile = 11,
- VideoTimestamp = 12,
- IsAnamorphic = 13,
- RefFrames = 14,
- NumAudioStreams = 16,
- NumVideoStreams = 17,
- IsSecondaryAudio = 18,
- VideoCodecTag = 19,
- IsAvc = 20,
- IsInterlaced = 21,
- AudioSampleRate = 22,
- AudioBitDepth = 23
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
deleted file mode 100644
index 8efdb0660..000000000
--- a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public class ResolutionConfiguration
- {
- public int MaxWidth { get; set; }
- public int MaxBitrate { get; set; }
-
- public ResolutionConfiguration(int maxWidth, int maxBitrate)
- {
- MaxWidth = maxWidth;
- MaxBitrate = maxBitrate;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
deleted file mode 100644
index de832314c..000000000
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ResolutionNormalizer
- {
- private static readonly ResolutionConfiguration[] Configurations =
- new []
- {
- new ResolutionConfiguration(426, 320000),
- new ResolutionConfiguration(640, 400000),
- new ResolutionConfiguration(720, 950000),
- new ResolutionConfiguration(1280, 2500000),
- new ResolutionConfiguration(1920, 4000000),
- new ResolutionConfiguration(3840, 35000000)
- };
-
- public static ResolutionOptions Normalize(int? inputBitrate,
- int outputBitrate,
- string inputCodec,
- string outputCodec,
- int? maxWidth,
- int? maxHeight)
- {
- // If the bitrate isn't changing, then don't downlscale the resolution
- if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value)
- {
- if (maxWidth.HasValue || maxHeight.HasValue)
- {
- return new ResolutionOptions
- {
- MaxWidth = maxWidth,
- MaxHeight = maxHeight
- };
- }
- }
-
- var resolutionConfig = GetResolutionConfiguration(outputBitrate);
- if (resolutionConfig != null)
- {
- var originvalValue = maxWidth;
-
- maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth);
- if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value)
- {
- maxHeight = null;
- }
- }
-
- return new ResolutionOptions
- {
- MaxWidth = maxWidth,
- MaxHeight = maxHeight
- };
- }
-
- private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate)
- {
- ResolutionConfiguration previousOption = null;
-
- foreach (var config in Configurations)
- {
- if (outputBitrate <= config.MaxBitrate)
- {
- return previousOption ?? config;
- }
-
- previousOption = config;
- }
-
- return null;
- }
-
- private static double GetVideoBitrateScaleFactor(string codec)
- {
- if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
- StringHelper.EqualsIgnoreCase(codec, "hevc") ||
- StringHelper.EqualsIgnoreCase(codec, "vp9"))
- {
- return .5;
- }
- return 1;
- }
-
- public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
- {
- var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
- var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
- var scaleFactor = outputScaleFactor/inputScaleFactor;
- var newBitrate = scaleFactor*bitrate;
-
- return Convert.ToInt32(newBitrate);
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
deleted file mode 100644
index 6b711cfa0..000000000
--- a/MediaBrowser.Model/Dlna/ResolutionOptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public class ResolutionOptions
- {
- public int? MaxWidth { get; set; }
- public int? MaxHeight { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs
deleted file mode 100644
index 742253fa3..000000000
--- a/MediaBrowser.Model/Dlna/ResponseProfile.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class ResponseProfile
- {
- [XmlAttribute("container")]
- public string Container { get; set; }
-
- [XmlAttribute("audioCodec")]
- public string AudioCodec { get; set; }
-
- [XmlAttribute("videoCodec")]
- public string VideoCodec { get; set; }
-
- [XmlAttribute("type")]
- public DlnaProfileType Type { get; set; }
-
- [XmlAttribute("orgPn")]
- public string OrgPn { get; set; }
-
- [XmlAttribute("mimeType")]
- public string MimeType { get; set; }
-
- public ProfileCondition[] Conditions { get; set; }
-
- public ResponseProfile()
- {
- Conditions = new ProfileCondition[] {};
- }
-
- public string[] GetContainers()
- {
- return ContainerProfile.SplitValue(Container);
- }
-
- public string[] GetAudioCodecs()
- {
- return ContainerProfile.SplitValue(AudioCodec);
- }
-
- public string[] GetVideoCodecs()
- {
- return ContainerProfile.SplitValue(VideoCodec);
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs
deleted file mode 100644
index 67c1b9042..000000000
--- a/MediaBrowser.Model/Dlna/SearchCriteria.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class SearchCriteria
- {
- public SearchType SearchType { get; set; }
-
- public SearchCriteria(string search)
- {
- if (string.IsNullOrEmpty(search))
- {
- throw new ArgumentNullException("search");
- }
-
- SearchType = SearchType.Unknown;
-
- String[] factors = StringHelper.RegexSplit(search, "(and|or)");
- foreach (String factor in factors)
- {
- String[] subFactors = StringHelper.RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
-
- if (subFactors.Length == 3)
- {
-
- if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) &&
- (StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1])))
- {
- if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2]))
- {
- SearchType = SearchType.Image;
- }
- else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2]))
- {
- SearchType = SearchType.Video;
- }
- else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2]))
- {
- SearchType = SearchType.Playlist;
- }
- else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2]))
- {
- SearchType = SearchType.MusicAlbum;
- }
- }
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/SearchType.cs b/MediaBrowser.Model/Dlna/SearchType.cs
deleted file mode 100644
index 27b207879..000000000
--- a/MediaBrowser.Model/Dlna/SearchType.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum SearchType
- {
- Unknown = 0,
- Audio = 1,
- Image = 2,
- Video = 3,
- Playlist = 4,
- MusicAlbum = 5
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs
deleted file mode 100644
index 600a2f58e..000000000
--- a/MediaBrowser.Model/Dlna/SortCriteria.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class SortCriteria
- {
- public SortOrder SortOrder
- {
- get { return SortOrder.Ascending; }
- }
-
- public SortCriteria(string value)
- {
-
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
deleted file mode 100644
index 2da6f1c81..000000000
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ /dev/null
@@ -1,1791 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class StreamBuilder
- {
- private readonly ILogger _logger;
- private readonly ITranscoderSupport _transcoderSupport;
-
- public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
- {
- _transcoderSupport = transcoderSupport;
- _logger = logger;
- }
-
- public StreamBuilder(ILogger logger)
- : this(new FullTranscoderSupport(), logger)
- {
- }
-
- public StreamInfo BuildAudioItem(AudioOptions options)
- {
- ValidateAudioInput(options);
-
- var mediaSources = new List<MediaSourceInfo>();
- foreach (MediaSourceInfo i in options.MediaSources)
- {
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId))
- {
- mediaSources.Add(i);
- }
- }
-
- var streams = new List<StreamInfo>();
- foreach (MediaSourceInfo i in mediaSources)
- {
- StreamInfo streamInfo = BuildAudioItem(i, options);
- if (streamInfo != null)
- {
- streams.Add(streamInfo);
- }
- }
-
- foreach (StreamInfo stream in streams)
- {
- stream.DeviceId = options.DeviceId;
- stream.DeviceProfileId = options.Profile.Id;
- }
-
- return GetOptimalStream(streams, options.GetMaxBitrate(true));
- }
-
- public StreamInfo BuildVideoItem(VideoOptions options)
- {
- ValidateInput(options);
-
- var mediaSources = new List<MediaSourceInfo>();
- foreach (MediaSourceInfo i in options.MediaSources)
- {
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId))
- {
- mediaSources.Add(i);
- }
- }
-
- var streams = new List<StreamInfo>();
- foreach (MediaSourceInfo i in mediaSources)
- {
- StreamInfo streamInfo = BuildVideoItem(i, options);
- if (streamInfo != null)
- {
- streams.Add(streamInfo);
- }
- }
-
- foreach (StreamInfo stream in streams)
- {
- stream.DeviceId = options.DeviceId;
- stream.DeviceProfileId = options.Profile.Id;
- }
-
- return GetOptimalStream(streams, options.GetMaxBitrate(false));
- }
-
- private StreamInfo GetOptimalStream(List<StreamInfo> streams, long? maxBitrate)
- {
- var sorted = StreamInfoSorter.SortMediaSources(streams, maxBitrate);
-
- foreach (StreamInfo stream in sorted)
- {
- return stream;
- }
-
- return null;
- }
-
- private TranscodeReason? GetTranscodeReasonForFailedCondition(ProfileCondition condition)
- {
- switch (condition.Property)
- {
- case ProfileConditionValue.AudioBitrate:
- if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- return TranscodeReason.AudioBitrateNotSupported;
- }
- return TranscodeReason.AudioBitrateNotSupported;
-
- case ProfileConditionValue.AudioChannels:
- if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- return TranscodeReason.AudioChannelsNotSupported;
- }
- return TranscodeReason.AudioChannelsNotSupported;
-
- case ProfileConditionValue.AudioProfile:
- return TranscodeReason.AudioProfileNotSupported;
-
- case ProfileConditionValue.AudioSampleRate:
- return TranscodeReason.AudioSampleRateNotSupported;
-
- case ProfileConditionValue.Has64BitOffsets:
- // TODO
- return null;
-
- case ProfileConditionValue.Height:
- return TranscodeReason.VideoResolutionNotSupported;
-
- case ProfileConditionValue.IsAnamorphic:
- return TranscodeReason.AnamorphicVideoNotSupported;
-
- case ProfileConditionValue.IsAvc:
- // TODO
- return null;
-
- case ProfileConditionValue.IsInterlaced:
- return TranscodeReason.InterlacedVideoNotSupported;
-
- case ProfileConditionValue.IsSecondaryAudio:
- return TranscodeReason.SecondaryAudioNotSupported;
-
- case ProfileConditionValue.NumAudioStreams:
- // TODO
- return null;
-
- case ProfileConditionValue.NumVideoStreams:
- // TODO
- return null;
-
- case ProfileConditionValue.PacketLength:
- // TODO
- return null;
-
- case ProfileConditionValue.RefFrames:
- return TranscodeReason.RefFramesNotSupported;
-
- case ProfileConditionValue.VideoBitDepth:
- return TranscodeReason.VideoBitDepthNotSupported;
-
- case ProfileConditionValue.AudioBitDepth:
- return TranscodeReason.AudioBitDepthNotSupported;
-
- case ProfileConditionValue.VideoBitrate:
- return TranscodeReason.VideoBitrateNotSupported;
-
- case ProfileConditionValue.VideoCodecTag:
- return TranscodeReason.VideoCodecNotSupported;
-
- case ProfileConditionValue.VideoFramerate:
- return TranscodeReason.VideoFramerateNotSupported;
-
- case ProfileConditionValue.VideoLevel:
- return TranscodeReason.VideoLevelNotSupported;
-
- case ProfileConditionValue.VideoProfile:
- return TranscodeReason.VideoProfileNotSupported;
-
- case ProfileConditionValue.VideoTimestamp:
- // TODO
- return null;
-
- case ProfileConditionValue.Width:
- return TranscodeReason.VideoResolutionNotSupported;
-
- default:
- return null;
- }
- }
-
- public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type)
- {
- if (string.IsNullOrWhiteSpace(inputContainer))
- {
- return null;
- }
-
- var formats = ContainerProfile.SplitValue(inputContainer);
-
- if (formats.Length == 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)
- {
- var transcodeReasons = new List<TranscodeReason>();
-
- StreamInfo playlistItem = new StreamInfo
- {
- ItemId = options.ItemId,
- MediaType = DlnaProfileType.Audio,
- MediaSource = item,
- RunTimeTicks = item.RunTimeTicks,
- Context = options.Context,
- DeviceProfile = options.Profile
- };
-
- if (options.ForceDirectPlay)
- {
- playlistItem.PlayMethod = PlayMethod.DirectPlay;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
- return playlistItem;
- }
-
- if (options.ForceDirectStream)
- {
- playlistItem.PlayMethod = PlayMethod.DirectStream;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
- return playlistItem;
- }
-
- MediaStream audioStream = item.GetDefaultAudioStream(null);
-
- var directPlayInfo = GetAudioDirectPlayMethods(item, audioStream, options);
-
- var directPlayMethods = directPlayInfo.Item1;
- transcodeReasons.AddRange(directPlayInfo.Item2);
-
- ConditionProcessor conditionProcessor = new ConditionProcessor();
-
- int? inputAudioChannels = audioStream == null ? null : audioStream.Channels;
- int? inputAudioBitrate = audioStream == null ? null : audioStream.BitDepth;
- int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
- int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
-
- if (directPlayMethods.Count > 0)
- {
- string audioCodec = audioStream == null ? null : audioStream.Codec;
-
- // Make sure audio codec profiles are satisfied
- if (!string.IsNullOrEmpty(audioCodec))
- {
- var conditions = new List<ProfileCondition>();
- foreach (CodecProfile i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.Audio && i.ContainsAnyCodec(audioCodec, item.Container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- if (!conditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
- }
-
- bool all = true;
- foreach (ProfileCondition c in conditions)
- {
- if (!conditionProcessor.IsAudioConditionSatisfied(c, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", c, item);
- var transcodeReason = GetTranscodeReasonForFailedCondition(c);
- if (transcodeReason.HasValue)
- {
- transcodeReasons.Add(transcodeReason.Value);
- }
- all = false;
- break;
- }
- }
-
- if (all)
- {
- if (directPlayMethods.Contains(PlayMethod.DirectStream))
- {
- playlistItem.PlayMethod = PlayMethod.DirectStream;
- }
-
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
-
- return playlistItem;
- }
- }
- }
-
- TranscodingProfile transcodingProfile = null;
- foreach (TranscodingProfile i in options.Profile.TranscodingProfiles)
- {
- if (i.Type == playlistItem.MediaType && i.Context == options.Context)
- {
- if (_transcoderSupport.CanEncodeToAudioCodec(i.AudioCodec ?? i.Container))
- {
- transcodingProfile = i;
- break;
- }
- }
- }
-
- if (transcodingProfile != null)
- {
- if (!item.SupportsTranscoding)
- {
- return null;
- }
-
- playlistItem.PlayMethod = PlayMethod.Transcode;
- playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
- playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
- playlistItem.Container = transcodingProfile.Container;
-
- if (string.IsNullOrEmpty(transcodingProfile.AudioCodec))
- {
- playlistItem.AudioCodecs = new string[] { };
- }
- else
- {
- playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(',');
- }
-
- playlistItem.SubProtocol = transcodingProfile.Protocol;
-
- var audioCodecProfiles = new List<CodecProfile>();
- foreach (CodecProfile i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.Audio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container))
- {
- audioCodecProfiles.Add(i);
- }
-
- if (audioCodecProfiles.Count >= 1) break;
- }
-
- var audioTranscodingConditions = new List<ProfileCondition>();
- foreach (CodecProfile i in audioCodecProfiles)
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- if (!conditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- audioTranscodingConditions.Add(c);
- }
- }
- }
-
- ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, false);
-
- // Honor requested max channels
- if (options.MaxAudioChannels.HasValue)
- {
- int currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value;
-
- playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
- }
-
- long transcodingBitrate = options.AudioTranscodingBitrate ??
- options.Profile.MusicStreamingTranscodingBitrate ??
- 128000;
-
- var configuredBitrate = options.GetMaxBitrate(true);
-
- if (configuredBitrate.HasValue)
- {
- transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
- }
-
- var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
- playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
- }
-
- playlistItem.TranscodeReasons = transcodeReasons;
- return playlistItem;
- }
-
- private long? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio)
- {
- if (item.Protocol == MediaProtocol.File)
- {
- return options.Profile.MaxStaticBitrate;
- }
-
- return options.GetMaxBitrate(isAudio);
- }
-
- private Tuple<List<PlayMethod>, List<TranscodeReason>> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
- {
- var transcodeReasons = new List<TranscodeReason>();
-
- DirectPlayProfile directPlayProfile = null;
- foreach (DirectPlayProfile i in options.Profile.DirectPlayProfiles)
- {
- if (i.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(i, item, audioStream))
- {
- directPlayProfile = i;
- break;
- }
- }
-
- var playMethods = new List<PlayMethod>();
-
- if (directPlayProfile != null)
- {
- // While options takes the network and other factors into account. Only applies to direct stream
- if (item.SupportsDirectStream)
- {
- if (IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true), PlayMethod.DirectStream))
- {
- if (options.EnableDirectStream)
- {
- playMethods.Add(PlayMethod.DirectStream);
- }
- }
- else
- {
- transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit);
- }
- }
-
- // The profile describes what the device supports
- // If device requirements are satisfied then allow both direct stream and direct play
- if (item.SupportsDirectPlay)
- {
- if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), PlayMethod.DirectPlay))
- {
- if (options.EnableDirectPlay)
- {
- playMethods.Add(PlayMethod.DirectPlay);
- }
- }
- else
- {
- transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit);
- }
- }
- }
- else
- {
- transcodeReasons.InsertRange(0, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
-
- _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}",
- options.Profile.Name ?? "Unknown Profile",
- item.Path ?? "Unknown path");
- }
-
- if (playMethods.Count > 0)
- {
- transcodeReasons.Clear();
- }
- else
- {
- transcodeReasons = transcodeReasons.Distinct().ToList();
- }
-
- return new Tuple<List<PlayMethod>, List<TranscodeReason>>(playMethods, transcodeReasons);
- }
-
- private List<TranscodeReason> GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<DirectPlayProfile> directPlayProfiles)
- {
- var list = new List<TranscodeReason>();
- var containerSupported = false;
- var audioSupported = false;
- var videoSupported = false;
-
- foreach (var profile in directPlayProfiles)
- {
- // Check container type
- if (profile.SupportsContainer(item.Container))
- {
- containerSupported = true;
-
- if (videoStream != null)
- {
- // Check video codec
- var videoCodecs = profile.GetVideoCodecs();
- if (videoCodecs.Length > 0)
- {
- string videoCodec = videoStream.Codec;
- if (!string.IsNullOrEmpty(videoCodec) && ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
- {
- videoSupported = true;
- }
- }
- else
- {
- videoSupported = true;
- }
- }
-
- if (audioStream != null)
- {
- // Check audio codec
- var audioCodecs = profile.GetAudioCodecs();
- if (audioCodecs.Length > 0)
- {
- string audioCodec = audioStream.Codec;
- if (!string.IsNullOrEmpty(audioCodec) && ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
- {
- audioSupported = true;
- }
- }
- else
- {
- audioSupported = true;
- }
- }
- }
- }
-
- if (!containerSupported)
- {
- list.Add(TranscodeReason.ContainerNotSupported);
- }
-
- if (videoStream != null && !videoSupported)
- {
- list.Add(TranscodeReason.VideoCodecNotSupported);
- }
-
- if (audioStream != null && !audioSupported)
- {
- list.Add(TranscodeReason.AudioCodecNotSupported);
- }
-
- return list;
- }
-
- private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles)
- {
- int highestScore = -1;
-
- foreach (MediaStream stream in item.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue)
- {
- if (stream.Score.Value > highestScore)
- {
- highestScore = stream.Score.Value;
- }
- }
- }
-
- var topStreams = new List<MediaStream>();
- foreach (MediaStream stream in item.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore)
- {
- topStreams.Add(stream);
- }
- }
-
- // If multiple streams have an equal score, try to pick the most efficient one
- if (topStreams.Count > 1)
- {
- foreach (MediaStream stream in topStreams)
- {
- foreach (SubtitleProfile profile in subtitleProfiles)
- {
- if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec))
- {
- return stream.Index;
- }
- }
- }
- }
-
- // If no optimization panned out, just use the original default
- return item.DefaultSubtitleStreamIndex;
- }
-
- private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- var transcodeReasons = new List<TranscodeReason>();
-
- StreamInfo playlistItem = new StreamInfo
- {
- ItemId = options.ItemId,
- MediaType = DlnaProfileType.Video,
- MediaSource = item,
- RunTimeTicks = item.RunTimeTicks,
- Context = options.Context,
- DeviceProfile = options.Profile
- };
-
- playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles);
- MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null;
-
- MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex);
- int? audioStreamIndex = null;
- if (audioStream != null)
- {
- audioStreamIndex = audioStream.Index;
- }
-
- MediaStream videoStream = item.VideoStream;
-
- // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
- var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay);
- var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream);
- bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1);
- bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1);
-
- _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
- options.Profile.Name ?? "Unknown Profile",
- item.Path ?? "Unknown path",
- isEligibleForDirectPlay,
- isEligibleForDirectStream);
-
- if (isEligibleForDirectPlay || isEligibleForDirectStream)
- {
- // See if it can be direct played
- var directPlayInfo = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream);
- var directPlay = directPlayInfo.Item1;
-
- if (directPlay != null)
- {
- playlistItem.PlayMethod = directPlay.Value;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video);
-
- if (subtitleStream != null)
- {
- SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, null, null);
-
- playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
- playlistItem.SubtitleFormat = subtitleProfile.Format;
- }
-
- return playlistItem;
- }
-
- transcodeReasons.AddRange(directPlayInfo.Item2);
- }
-
- if (directPlayEligibilityResult.Item2.HasValue)
- {
- transcodeReasons.Add(directPlayEligibilityResult.Item2.Value);
- }
-
- if (directStreamEligibilityResult.Item2.HasValue)
- {
- transcodeReasons.Add(directStreamEligibilityResult.Item2.Value);
- }
-
- // Can't direct play, find the transcoding profile
- TranscodingProfile transcodingProfile = null;
- foreach (TranscodingProfile i in options.Profile.TranscodingProfiles)
- {
- if (i.Type == playlistItem.MediaType && i.Context == options.Context)
- {
- transcodingProfile = i;
- break;
- }
- }
-
- if (transcodingProfile != null)
- {
- if (!item.SupportsTranscoding)
- {
- return null;
- }
-
- if (subtitleStream != null)
- {
- SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Protocol, transcodingProfile.Container);
-
- playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
- playlistItem.SubtitleFormat = subtitleProfile.Format;
- playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
- }
-
- playlistItem.PlayMethod = PlayMethod.Transcode;
- playlistItem.Container = transcodingProfile.Container;
- playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
- playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
- playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(',');
-
- playlistItem.VideoCodecs = transcodingProfile.VideoCodec.Split(',');
- playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
- playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
-
- playlistItem.BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames;
-
- if (transcodingProfile.MinSegments > 0)
- {
- playlistItem.MinSegments = transcodingProfile.MinSegments;
- }
- if (transcodingProfile.SegmentLength > 0)
- {
- playlistItem.SegmentLength = transcodingProfile.SegmentLength;
- }
-
- if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels))
- {
- int transcodingMaxAudioChannels;
- if (int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out transcodingMaxAudioChannels))
- {
- playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels;
- }
- }
- playlistItem.SubProtocol = transcodingProfile.Protocol;
- playlistItem.AudioStreamIndex = audioStreamIndex;
- ConditionProcessor conditionProcessor = new ConditionProcessor();
-
- var isFirstAppliedCodecProfile = true;
- foreach (CodecProfile i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.Video && i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- int? width = videoStream == null ? null : videoStream.Width;
- int? height = videoStream == null ? null : videoStream.Height;
- int? bitDepth = videoStream == null ? null : videoStream.BitDepth;
- int? videoBitrate = videoStream == null ? null : videoStream.BitRate;
- double? videoLevel = videoStream == null ? null : videoStream.Level;
- string videoProfile = videoStream == null ? null : videoStream.Profile;
- float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
- bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
- bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced;
- string videoCodecTag = videoStream == null ? null : videoStream.CodecTag;
- bool? isAvc = videoStream == null ? null : videoStream.IsAVC;
-
- TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
- int? packetLength = videoStream == null ? null : videoStream.PacketLength;
- int? refFrames = videoStream == null ? null : videoStream.RefFrames;
-
- int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
- int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
-
- if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- var transcodingVideoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
- foreach (var transcodingVideoCodec in transcodingVideoCodecs)
- {
- if (i.ContainsAnyCodec(transcodingVideoCodec, transcodingProfile.Container))
- {
- ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, !isFirstAppliedCodecProfile);
- isFirstAppliedCodecProfile = false;
- }
- }
- }
- }
- }
-
- var audioTranscodingConditions = new List<ProfileCondition>();
- foreach (CodecProfile i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
- int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
- int? audioChannels = audioStream == null ? null : audioStream.Channels;
- string audioProfile = audioStream == null ? null : audioStream.Profile;
- int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
- int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
-
- if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
- {
- LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- audioTranscodingConditions.Add(c);
- }
- break;
- }
- }
- }
- // Honor requested max channels
- if (options.MaxAudioChannels.HasValue)
- {
- int currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value;
-
- playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
- }
-
- int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
- playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
-
- var maxBitrateSetting = options.GetMaxBitrate(false);
- // Honor max rate
- if (maxBitrateSetting.HasValue)
- {
- var videoBitrate = maxBitrateSetting.Value;
-
- if (playlistItem.AudioBitrate.HasValue)
- {
- videoBitrate -= playlistItem.AudioBitrate.Value;
- }
-
- // Make sure the video bitrate is lower than bitrate settings but at least 64k
- long currentValue = playlistItem.VideoBitrate ?? videoBitrate;
- var longBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000);
- playlistItem.VideoBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
- }
-
- // Do this after initial values are set to account for greater than/less than conditions
- ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, false);
- }
-
- playlistItem.TranscodeReasons = transcodeReasons;
-
- return playlistItem;
- }
-
- private int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
- {
- if ((audioStream.Channels ?? 0) >= 6)
- {
- return 384000;
- }
-
- return 192000;
- }
-
- private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string[] targetAudioCodecs, MediaStream audioStream)
- {
- var targetAudioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
-
- int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
-
- // Reduce the bitrate if we're downmixing
- if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
- {
- defaultBitrate = StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3") ? 192000 : 128000;
- }
-
- if (StringHelper.EqualsIgnoreCase(subProtocol, "hls"))
- {
- defaultBitrate = Math.Min(384000, defaultBitrate);
- }
- else
- {
- defaultBitrate = Math.Min(448000, defaultBitrate);
- }
-
- int encoderAudioBitrateLimit = int.MaxValue;
-
- if (audioStream != null)
- {
- // Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
- // Any attempts to transcode over 64k will fail
- if (audioStream.Channels.HasValue &&
- audioStream.Channels.Value == 1)
- {
- if ((audioStream.BitRate ?? 0) < 64000)
- {
- encoderAudioBitrateLimit = 64000;
- }
- }
- }
-
- if (maxTotalBitrate.HasValue)
- {
- if (maxTotalBitrate.Value < 640000)
- {
- defaultBitrate = Math.Min(128000, defaultBitrate);
- }
- }
-
- return Math.Min(defaultBitrate, encoderAudioBitrateLimit);
- }
-
- private Tuple<PlayMethod?, List<TranscodeReason>> GetVideoDirectPlayProfile(VideoOptions options,
- MediaSourceInfo mediaSource,
- MediaStream videoStream,
- MediaStream audioStream,
- bool isEligibleForDirectPlay,
- bool isEligibleForDirectStream)
- {
- DeviceProfile profile = options.Profile;
-
- if (options.ForceDirectPlay)
- {
- return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectPlay, new List<TranscodeReason>());
- }
- if (options.ForceDirectStream)
- {
- return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectStream, new List<TranscodeReason>());
- }
-
- // See if it can be direct played
- DirectPlayProfile directPlay = null;
- foreach (DirectPlayProfile i in profile.DirectPlayProfiles)
- {
- if (i.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(i, mediaSource, videoStream, audioStream))
- {
- directPlay = i;
- break;
- }
- }
-
- if (directPlay == null)
- {
- _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}",
- profile.Name ?? "Unknown Profile",
- mediaSource.Path ?? "Unknown path");
-
- return new Tuple<PlayMethod?, List<TranscodeReason>>(null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles));
- }
-
- string container = mediaSource.Container;
-
- var conditions = new List<ProfileCondition>();
- foreach (ContainerProfile i in profile.ContainerProfiles)
- {
- if (i.Type == DlnaProfileType.Video &&
- i.ContainsContainer(container))
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
-
- ConditionProcessor conditionProcessor = new ConditionProcessor();
-
- int? width = videoStream == null ? null : videoStream.Width;
- int? height = videoStream == null ? null : videoStream.Height;
- int? bitDepth = videoStream == null ? null : videoStream.BitDepth;
- int? videoBitrate = videoStream == null ? null : videoStream.BitRate;
- double? videoLevel = videoStream == null ? null : videoStream.Level;
- string videoProfile = videoStream == null ? null : videoStream.Profile;
- float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
- bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
- bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced;
- string videoCodecTag = videoStream == null ? null : videoStream.CodecTag;
- bool? isAvc = videoStream == null ? null : videoStream.IsAVC;
-
- int? audioBitrate = audioStream == null ? null : audioStream.BitRate;
- int? audioChannels = audioStream == null ? null : audioStream.Channels;
- string audioProfile = audioStream == null ? null : audioStream.Profile;
- int? audioSampleRate = audioStream == null ? null : audioStream.SampleRate;
- int? audioBitDepth = audioStream == null ? null : audioStream.BitDepth;
-
- TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
- int? packetLength = videoStream == null ? null : videoStream.PacketLength;
- int? refFrames = videoStream == null ? null : videoStream.RefFrames;
-
- int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
- int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
-
- // Check container conditions
- foreach (ProfileCondition i in conditions)
- {
- if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource);
-
- var transcodeReason = GetTranscodeReasonForFailedCondition(i);
- var transcodeReasons = transcodeReason.HasValue
- ? new List<TranscodeReason> { transcodeReason.Value }
- : new List<TranscodeReason> { };
-
- return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons);
- }
- }
-
- string videoCodec = videoStream == null ? null : videoStream.Codec;
-
- conditions = new List<ProfileCondition>();
- foreach (CodecProfile i in profile.CodecProfiles)
- {
- if (i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(profile, "VideoCodecProfile", applyCondition, mediaSource);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
- }
-
- foreach (ProfileCondition i in conditions)
- {
- if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource);
-
- var transcodeReason = GetTranscodeReasonForFailedCondition(i);
- var transcodeReasons = transcodeReason.HasValue
- ? new List<TranscodeReason> { transcodeReason.Value }
- : new List<TranscodeReason> { };
-
- return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons);
- }
- }
-
- if (audioStream != null)
- {
- string audioCodec = audioStream.Codec;
-
- conditions = new List<ProfileCondition>();
- bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream);
-
- foreach (CodecProfile i in profile.CodecProfiles)
- {
- if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(audioCodec, container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio))
- {
- LogConditionFailure(profile, "VideoAudioCodecProfile.ApplyConditions", applyCondition, mediaSource);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
- }
-
- foreach (ProfileCondition i in conditions)
- {
- if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio))
- {
- LogConditionFailure(profile, "VideoAudioCodecProfile", i, mediaSource);
-
- var transcodeReason = GetTranscodeReasonForFailedCondition(i);
- var transcodeReasons = transcodeReason.HasValue
- ? new List<TranscodeReason> { transcodeReason.Value }
- : new List<TranscodeReason> { };
-
- return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons);
- }
- }
- }
-
- if (isEligibleForDirectStream && mediaSource.SupportsDirectStream)
- {
- return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectStream, new List<TranscodeReason>());
- }
-
- return new Tuple<PlayMethod?, List<TranscodeReason>>(null, new List<TranscodeReason> { TranscodeReason.ContainerBitrateExceedsLimit });
- }
-
- private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource)
- {
- _logger.Info("Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}",
- type,
- profile.Name ?? "Unknown Profile",
- condition.Property,
- condition.Condition,
- condition.Value ?? string.Empty,
- condition.IsRequired,
- mediaSource.Path ?? "Unknown path");
- }
-
- private Tuple<bool, TranscodeReason?> IsEligibleForDirectPlay(MediaSourceInfo item,
- long? maxBitrate,
- MediaStream subtitleStream,
- VideoOptions options,
- PlayMethod playMethod)
- {
- if (subtitleStream != null)
- {
- SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, null, null);
-
- if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
- {
- _logger.Info("Not eligible for {0} due to unsupported subtitles", playMethod);
- return new Tuple<bool, TranscodeReason?>(false, TranscodeReason.SubtitleCodecNotSupported);
- }
- }
-
- var result = IsAudioEligibleForDirectPlay(item, maxBitrate, playMethod);
-
- if (result)
- {
- return new Tuple<bool, TranscodeReason?>(result, null);
- }
-
- return new Tuple<bool, TranscodeReason?>(result, TranscodeReason.ContainerBitrateExceedsLimit);
- }
-
- public static SubtitleProfile GetSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, string transcodingSubProtocol, string transcodingContainer)
- {
- if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || !string.Equals(transcodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)))
- {
- // Look for supported embedded subs of the same format
- foreach (SubtitleProfile profile in subtitleProfiles)
- {
- if (!profile.SupportsLanguage(subtitleStream.Language))
- {
- continue;
- }
-
- if (profile.Method != SubtitleDeliveryMethod.Embed)
- {
- continue;
- }
-
- if (playMethod == PlayMethod.Transcode && !IsSubtitleEmbedSupported(subtitleStream, profile, transcodingSubProtocol, transcodingContainer))
- {
- continue;
- }
-
- if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && StringHelper.EqualsIgnoreCase(profile.Format, subtitleStream.Codec))
- {
- return profile;
- }
- }
-
- // Look for supported embedded subs of a convertible format
- foreach (SubtitleProfile profile in subtitleProfiles)
- {
- if (!profile.SupportsLanguage(subtitleStream.Language))
- {
- continue;
- }
-
- if (profile.Method != SubtitleDeliveryMethod.Embed)
- {
- continue;
- }
-
- if (playMethod == PlayMethod.Transcode && !IsSubtitleEmbedSupported(subtitleStream, profile, transcodingSubProtocol, transcodingContainer))
- {
- continue;
- }
-
- if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format))
- {
- return profile;
- }
- }
- }
-
- // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion
- return GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ??
- GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
- new SubtitleProfile
- {
- Method = SubtitleDeliveryMethod.Encode,
- Format = subtitleStream.Codec
- };
- }
-
- private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer)
- {
- if (!string.IsNullOrWhiteSpace(transcodingContainer))
- {
- 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;
- }
-
- private static SubtitleProfile GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
- {
- foreach (SubtitleProfile profile in subtitleProfiles)
- {
- if (profile.Method != SubtitleDeliveryMethod.External && profile.Method != SubtitleDeliveryMethod.Hls)
- {
- continue;
- }
-
- if (profile.Method == SubtitleDeliveryMethod.Hls && playMethod != PlayMethod.Transcode)
- {
- continue;
- }
-
- if (!profile.SupportsLanguage(subtitleStream.Language))
- {
- continue;
- }
-
- if (!subtitleStream.IsExternal && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec))
- {
- continue;
- }
-
- if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
- (profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
- {
- bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
-
- if (!requiresConversion)
- {
- return profile;
- }
-
- if (!allowConversion)
- {
- continue;
- }
-
- // TODO: Build this into subtitleStream.SupportsExternalStream
- if (mediaSource.IsInfiniteStream)
- {
- continue;
- }
-
- if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format))
- {
- return profile;
- }
- }
- }
-
- return null;
- }
-
- private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, long? maxBitrate, PlayMethod playMethod)
- {
- // Don't restrict by bitrate if coming from an external domain
- if (item.IsRemote)
- {
- return true;
- }
-
- var requestedMaxBitrate = maxBitrate ?? 1000000;
-
- // If we don't know the bitrate, then force a transcode if requested max bitrate is under 40 mbps
- var itemBitrate = item.Bitrate ??
- 40000000;
-
- if (itemBitrate > requestedMaxBitrate)
- {
- _logger.Info("Bitrate exceeds " + playMethod + " limit: media bitrate: {0}, max bitrate: {1}", itemBitrate.ToString(CultureInfo.InvariantCulture), requestedMaxBitrate.ToString(CultureInfo.InvariantCulture));
- return false;
- }
-
- return true;
- }
-
- private void ValidateInput(VideoOptions options)
- {
- ValidateAudioInput(options);
-
- if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
- {
- throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
- }
-
- if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
- {
- throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
- }
- }
-
- private void ValidateAudioInput(AudioOptions options)
- {
- if (string.IsNullOrEmpty(options.ItemId))
- {
- throw new ArgumentException("ItemId is required");
- }
- if (string.IsNullOrEmpty(options.DeviceId))
- {
- throw new ArgumentException("DeviceId is required");
- }
- if (options.Profile == null)
- {
- throw new ArgumentException("Profile is required");
- }
- if (options.MediaSources == null)
- {
- throw new ArgumentException("MediaSources is required");
- }
- }
-
- private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool qualifiedOnly)
- {
- foreach (ProfileCondition condition in conditions)
- {
- string value = condition.Value;
-
- if (string.IsNullOrEmpty(value))
- {
- continue;
- }
-
- // No way to express this
- if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- continue;
- }
-
- switch (condition.Property)
- {
- case ProfileConditionValue.AudioBitrate:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.AudioBitrate = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.AudioBitrate = Math.Min(num, item.AudioBitrate ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.AudioBitrate = Math.Max(num, item.AudioBitrate ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.AudioChannels:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.MaxAudioChannels = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.MaxAudioChannels = Math.Min(num, item.MaxAudioChannels ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.MaxAudioChannels = Math.Max(num, item.MaxAudioChannels ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.IsAvc:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- bool isAvc;
- if (bool.TryParse(value, out isAvc))
- {
- if (isAvc && condition.Condition == ProfileConditionType.Equals)
- {
- item.RequireAvc = true;
- }
- else if (!isAvc && condition.Condition == ProfileConditionType.NotEquals)
- {
- item.RequireAvc = true;
- }
- }
- break;
- }
- case ProfileConditionValue.IsAnamorphic:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- bool isAnamorphic;
- if (bool.TryParse(value, out isAnamorphic))
- {
- if (isAnamorphic && condition.Condition == ProfileConditionType.Equals)
- {
- item.RequireNonAnamorphic = true;
- }
- else if (!isAnamorphic && condition.Condition == ProfileConditionType.NotEquals)
- {
- item.RequireNonAnamorphic = true;
- }
- }
- break;
- }
- case ProfileConditionValue.IsInterlaced:
- {
- if (string.IsNullOrWhiteSpace(qualifier))
- {
- continue;
- }
-
- bool isInterlaced;
- if (bool.TryParse(value, out isInterlaced))
- {
- if (!isInterlaced && condition.Condition == ProfileConditionType.Equals)
- {
- item.SetOption(qualifier, "deinterlace", "true");
- }
- else if (isInterlaced && condition.Condition == ProfileConditionType.NotEquals)
- {
- item.SetOption(qualifier, "deinterlace", "true");
- }
- }
- break;
- }
- case ProfileConditionValue.AudioProfile:
- case ProfileConditionValue.Has64BitOffsets:
- case ProfileConditionValue.PacketLength:
- case ProfileConditionValue.NumAudioStreams:
- case ProfileConditionValue.NumVideoStreams:
- case ProfileConditionValue.IsSecondaryAudio:
- case ProfileConditionValue.VideoTimestamp:
- {
- // Not supported yet
- break;
- }
- case ProfileConditionValue.RefFrames:
- {
- if (string.IsNullOrWhiteSpace(qualifier))
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(num));
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(Math.Min(num, item.GetTargetRefFrames(qualifier) ?? num)));
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetRefFrames(qualifier) ?? num)));
- }
- }
- break;
- }
- case ProfileConditionValue.VideoBitDepth:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.MaxVideoBitDepth = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.MaxVideoBitDepth = Math.Min(num, item.MaxVideoBitDepth ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.MaxVideoBitDepth = Math.Max(num, item.MaxVideoBitDepth ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.VideoProfile:
- {
- if (string.IsNullOrWhiteSpace(qualifier))
- {
- continue;
- }
-
- if (!string.IsNullOrWhiteSpace(value))
- {
- // change from split by | to comma
-
- // strip spaces to avoid having to encode
- var values = value
- .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
-
- item.SetOption(qualifier, "profile", string.Join(",", values));
- }
- break;
- }
- case ProfileConditionValue.Height:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.MaxHeight = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.MaxHeight = Math.Min(num, item.MaxHeight ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.MaxHeight = Math.Max(num, item.MaxHeight ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.VideoBitrate:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.VideoBitrate = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.VideoBitrate = Math.Min(num, item.VideoBitrate ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.VideoBitrate = Math.Max(num, item.VideoBitrate ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.VideoFramerate:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- float num;
- if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.MaxFramerate = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.MaxFramerate = Math.Min(num, item.MaxFramerate ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.MaxFramerate = Math.Max(num, item.MaxFramerate ?? num);
- }
- }
- break;
- }
- case ProfileConditionValue.VideoLevel:
- {
- if (string.IsNullOrWhiteSpace(qualifier))
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(num));
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(Math.Min(num, item.GetTargetVideoLevel(qualifier) ?? num)));
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetVideoLevel(qualifier) ?? num)));
- }
- }
- break;
- }
- case ProfileConditionValue.Width:
- {
- if (qualifiedOnly)
- {
- continue;
- }
-
- int num;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
- {
- if (condition.Condition == ProfileConditionType.Equals)
- {
- item.MaxWidth = num;
- }
- else if (condition.Condition == ProfileConditionType.LessThanEqual)
- {
- item.MaxWidth = Math.Min(num, item.MaxWidth ?? num);
- }
- else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
- {
- item.MaxWidth = Math.Max(num, item.MaxWidth ?? num);
- }
- }
- break;
- }
- default:
- break;
- }
- }
- }
-
- private bool IsAudioDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream audioStream)
- {
- // Check container type
- if (!profile.SupportsContainer(item.Container))
- {
- return false;
- }
-
- // Check audio codec
- var audioCodecs = profile.GetAudioCodecs();
- if (audioCodecs.Length > 0)
- {
- // Check audio codecs
- string audioCodec = audioStream == null ? null : audioStream.Codec;
- if (string.IsNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
- {
- return false;
- }
- }
-
- return true;
- }
-
- private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream)
- {
- // Check container type
- if (!profile.SupportsContainer(item.Container))
- {
- return false;
- }
-
- // Check video codec
- var videoCodecs = profile.GetVideoCodecs();
- if (videoCodecs.Length > 0)
- {
- string videoCodec = videoStream == null ? null : videoStream.Codec;
- if (string.IsNullOrEmpty(videoCodec) || !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
- {
- return false;
- }
- }
-
- // Check audio codec
- if (audioStream != null)
- {
- var audioCodecs = profile.GetAudioCodecs();
- if (audioCodecs.Length > 0)
- {
- // Check audio codecs
- string audioCodec = audioStream == null ? null : audioStream.Codec;
- if (string.IsNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
- {
- return false;
- }
- }
- }
-
- return true;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
deleted file mode 100644
index 6ded1f6dd..000000000
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ /dev/null
@@ -1,1056 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-
-namespace MediaBrowser.Model.Dlna
-{
- /// <summary>
- /// Class StreamInfo.
- /// </summary>
- public class StreamInfo
- {
- public StreamInfo()
- {
- AudioCodecs = new string[] { };
- VideoCodecs = new string[] { };
- SubtitleCodecs = new string[] { };
- TranscodeReasons = new List<TranscodeReason>();
- StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
- public void SetOption(string qualifier, string name, string value)
- {
- SetOption(qualifier + "-" + name, value);
- }
-
- public void SetOption(string name, string value)
- {
- StreamOptions[name] = value;
- }
-
- public string GetOption(string qualifier, string name)
- {
- return GetOption(qualifier + "-" + name);
- }
-
- public string GetOption(string name)
- {
- string value;
- if (StreamOptions.TryGetValue(name, out value))
- {
- return value;
- }
-
- return null;
- }
-
- public string ItemId { get; set; }
-
- public PlayMethod PlayMethod { get; set; }
- public EncodingContext Context { get; set; }
-
- public DlnaProfileType MediaType { get; set; }
-
- public string Container { get; set; }
-
- public string SubProtocol { get; set; }
-
- public long StartPositionTicks { get; set; }
-
- public int? SegmentLength { get; set; }
- public int? MinSegments { get; set; }
- public bool BreakOnNonKeyFrames { get; set; }
-
- public bool RequireAvc { get; set; }
- public bool RequireNonAnamorphic { get; set; }
- public bool CopyTimestamps { get; set; }
- public bool EnableSubtitlesInManifest { get; set; }
- public string[] AudioCodecs { get; set; }
- public string[] VideoCodecs { get; set; }
-
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public int? TranscodingMaxAudioChannels { get; set; }
- public int? MaxAudioChannels { get; set; }
-
- public int? AudioBitrate { get; set; }
-
- public int? VideoBitrate { get; set; }
-
- public int? MaxWidth { get; set; }
- public int? MaxHeight { get; set; }
-
- public int? MaxVideoBitDepth { get; set; }
-
- public float? MaxFramerate { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
- public string DeviceProfileId { get; set; }
- public string DeviceId { get; set; }
-
- public long? RunTimeTicks { get; set; }
-
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-
- public bool EstimateContentLength { get; set; }
-
- public MediaSourceInfo MediaSource { get; set; }
-
- public string[] SubtitleCodecs { get; set; }
- public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
- public string SubtitleFormat { get; set; }
-
- public string PlaySessionId { get; set; }
- public List<MediaSourceInfo> AllMediaSources { get; set; }
- public List<TranscodeReason> TranscodeReasons { get; set; }
-
- public Dictionary<string, string> StreamOptions { get; private set; }
-
- public string MediaSourceId
- {
- get
- {
- return MediaSource == null ? null : MediaSource.Id;
- }
- }
-
- public bool IsDirectStream
- {
- get
- {
- return PlayMethod == PlayMethod.DirectStream ||
- PlayMethod == PlayMethod.DirectPlay;
- }
- }
-
- public string ToUrl(string baseUrl, string accessToken)
- {
- if (PlayMethod == PlayMethod.DirectPlay)
- {
- return MediaSource.Path;
- }
-
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(baseUrl);
- }
-
- List<string> list = new List<string>();
- foreach (NameValuePair pair in BuildParams(this, accessToken, false))
- {
- if (string.IsNullOrEmpty(pair.Value))
- {
- continue;
- }
-
- // Try to keep the url clean by omitting defaults
- if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "0"))
- {
- continue;
- }
- if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "-1"))
- {
- continue;
- }
- if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "false"))
- {
- continue;
- }
-
- var encodedValue = pair.Value.Replace(" ", "%20");
-
- list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
- }
-
- string queryString = string.Join("&", list.ToArray(list.Count));
-
- return GetUrl(baseUrl, queryString);
- }
-
- public string ToDlnaUrl(string baseUrl, string accessToken)
- {
- if (PlayMethod == PlayMethod.DirectPlay)
- {
- return MediaSource.Path;
- }
-
- if (string.IsNullOrWhiteSpace(PlaySessionId))
- {
- PlaySessionId = Guid.NewGuid().ToString("N");
- }
-
- string dlnaCommand = BuildDlnaParam(this, accessToken);
- return GetUrl(baseUrl, dlnaCommand);
- }
-
- private string GetUrl(string baseUrl, string queryString)
- {
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(baseUrl);
- }
-
- string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
-
- baseUrl = baseUrl.TrimEnd('/');
-
- if (MediaType == DlnaProfileType.Audio)
- {
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
- {
- return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
- }
-
- return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
- }
-
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
- {
- return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
- }
-
- return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
- }
-
- private static string BuildDlnaParam(StreamInfo item, string accessToken)
- {
- List<string> list = new List<string>();
-
- foreach (NameValuePair pair in BuildParams(item, accessToken, true))
- {
- list.Add(pair.Value);
- }
-
- return string.Format("Params={0}", string.Join(";", list.ToArray(list.Count)));
- }
-
- private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken, bool isDlna)
- {
- List<NameValuePair> list = new List<NameValuePair>();
-
- string audioCodecs = item.AudioCodecs.Length == 0 ?
- string.Empty :
- string.Join(",", item.AudioCodecs);
-
- string videoCodecs = item.VideoCodecs.Length == 0 ?
- string.Empty :
- string.Join(",", item.VideoCodecs);
-
- list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty));
- list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty));
- list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty));
- list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower()));
- list.Add(new NameValuePair("VideoCodec", videoCodecs));
- list.Add(new NameValuePair("AudioCodec", audioCodecs));
- list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty));
- list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty));
- list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty));
- list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty));
- list.Add(new NameValuePair("MaxAudioChannels", item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty));
- list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty));
- list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty));
- list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty));
-
- long startPositionTicks = item.StartPositionTicks;
-
- var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls");
-
- if (isHls)
- {
- list.Add(new NameValuePair("StartTimeTicks", string.Empty));
- }
- else
- {
- list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks)));
- }
-
- if (isDlna)
- {
- // hack alert
- // dlna needs to be update to support the qualified params
- var level = item.GetTargetVideoLevel("h264");
-
- list.Add(new NameValuePair("Level", level.HasValue ? StringHelper.ToStringCultureInvariant(level.Value) : string.Empty));
- }
-
- if (isDlna)
- {
- // hack alert
- // dlna needs to be update to support the qualified params
- var refframes = item.GetTargetRefFrames("h264");
-
- list.Add(new NameValuePair("MaxRefFrames", refframes.HasValue ? StringHelper.ToStringCultureInvariant(refframes.Value) : string.Empty));
- }
-
- list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty));
-
- if (isDlna)
- {
- // hack alert
- // dlna needs to be update to support the qualified params
- var profile = item.GetOption("h264", "profile");
-
- // Avoid having to encode
- profile = (profile ?? string.Empty).Replace(" ", "");
-
- list.Add(new NameValuePair("Profile", profile));
- }
-
- // no longer used
- list.Add(new NameValuePair("Cabac", string.Empty));
-
- list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
- list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
-
- string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId;
- list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
-
- if (isDlna)
- {
- list.Add(new NameValuePair("ItemId", item.ItemId));
- }
-
- list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower()));
- list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
-
- list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty));
- list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower()));
-
- list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
- list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower()));
-
- string subtitleCodecs = item.SubtitleCodecs.Length == 0 ?
- string.Empty :
- string.Join(",", item.SubtitleCodecs);
-
- list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
-
- list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower()));
-
- if (isDlna)
- {
- // hack alert
- // dlna needs to be update to support the qualified params
- var deinterlace = string.Equals(item.GetOption("h264", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(item.GetOption("mpeg2video", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase);
-
- list.Add(new NameValuePair("DeInterlace", deinterlace.ToString().ToLower()));
- }
-
- if (!isDlna && isHls)
- {
- list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty));
-
- if (item.SegmentLength.HasValue)
- {
- list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture)));
- }
-
- if (item.MinSegments.HasValue)
- {
- list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture)));
- }
-
- list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString()));
- }
-
- if (isDlna || !item.IsDirectStream)
- {
- list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray())));
- }
-
- if (!isDlna)
- {
- foreach (var pair in item.StreamOptions)
- {
- if (string.IsNullOrWhiteSpace(pair.Value))
- {
- continue;
- }
-
- // strip spaces to avoid having to encode h264 profile names
- list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", "")));
- }
- }
-
- return list;
- }
-
- public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
- {
- return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
- }
-
- public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
- {
- List<SubtitleStreamInfo> list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
- List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>();
-
- // First add the selected track
- foreach (SubtitleStreamInfo stream in list)
- {
- if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- newList.Add(stream);
- }
- }
-
- return newList;
- }
-
- public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
- {
- return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
- }
-
- public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
- {
- List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
-
- // HLS will preserve timestamps so we can just grab the full subtitle stream
- long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls")
- ? 0
- : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
-
- // First add the selected track
- if (SubtitleStreamIndex.HasValue)
- {
- foreach (MediaStream stream in MediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
- {
- AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
- }
- }
- }
-
- if (!includeSelectedTrackOnly)
- {
- foreach (MediaStream stream in MediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
- {
- AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
- }
- }
- }
-
- return list;
- }
-
- private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
- {
- if (enableAllProfiles)
- {
- foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles)
- {
- SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
-
- list.Add(info);
- }
- }
- else
- {
- SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
-
- list.Add(info);
- }
- }
-
- private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
- {
- SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, SubProtocol, Container);
- SubtitleStreamInfo info = new SubtitleStreamInfo
- {
- IsForced = stream.IsForced,
- Language = stream.Language,
- Name = stream.Language ?? "Unknown",
- Format = subtitleProfile.Format,
- Index = stream.Index,
- DeliveryMethod = subtitleProfile.Method,
- DisplayTitle = stream.DisplayTitle
- };
-
- if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal)
- {
- info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
- baseUrl,
- ItemId,
- MediaSourceId,
- StringHelper.ToStringCultureInvariant(stream.Index),
- StringHelper.ToStringCultureInvariant(startPositionTicks),
- subtitleProfile.Format);
-
- if (!string.IsNullOrEmpty(accessToken))
- {
- info.Url += "?api_key=" + accessToken;
- }
-
- info.IsExternalUrl = false;
- }
- else
- {
- info.Url = stream.Path;
- info.IsExternalUrl = true;
- }
- }
-
- return info;
- }
-
- /// <summary>
- /// Returns the audio stream that will be used
- /// </summary>
- public MediaStream TargetAudioStream
- {
- get
- {
- if (MediaSource != null)
- {
- return MediaSource.GetDefaultAudioStream(AudioStreamIndex);
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Returns the video stream that will be used
- /// </summary>
- public MediaStream TargetVideoStream
- {
- get
- {
- if (MediaSource != null)
- {
- return MediaSource.VideoStream;
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetAudioSampleRate
- {
- get
- {
- MediaStream stream = TargetAudioStream;
- return stream == null ? null : stream.SampleRate;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetAudioBitDepth
- {
- get
- {
- MediaStream stream = TargetAudioStream;
- return stream == null ? null : stream.BitDepth;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetVideoBitDepth
- {
- get
- {
- MediaStream stream = TargetVideoStream;
- return stream == null || !IsDirectStream ? null : stream.BitDepth;
- }
- }
-
- /// <summary>
- /// Gets the target reference frames.
- /// </summary>
- /// <value>The target reference frames.</value>
- public int? TargetRefFrames
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
- }
-
- var targetVideoCodecs = TargetVideoCodec;
- var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
- if (!string.IsNullOrWhiteSpace(videoCodec))
- {
- return GetTargetRefFrames(videoCodec);
- }
-
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public float? TargetFramerate
- {
- get
- {
- MediaStream stream = TargetVideoStream;
- return MaxFramerate.HasValue && !IsDirectStream
- ? MaxFramerate
- : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public double? TargetVideoLevel
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
- }
-
- var targetVideoCodecs = TargetVideoCodec;
- var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
- if (!string.IsNullOrWhiteSpace(videoCodec))
- {
- return GetTargetVideoLevel(videoCodec);
- }
-
- return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
- }
- }
-
- public double? GetTargetVideoLevel(string codec)
- {
- var value = GetOption(codec, "level");
- if (string.IsNullOrWhiteSpace(value))
- {
- return null;
- }
-
- double result;
- if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
- {
- return result;
- }
-
- return null;
- }
-
- public int? GetTargetRefFrames(string codec)
- {
- var value = GetOption(codec, "maxrefframes");
- if (string.IsNullOrWhiteSpace(value))
- {
- return null;
- }
-
- int result;
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
- {
- return result;
- }
-
- return null;
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public int? TargetPacketLength
- {
- get
- {
- MediaStream stream = TargetVideoStream;
- return !IsDirectStream
- ? null
- : stream == null ? null : stream.PacketLength;
- }
- }
-
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public string TargetVideoProfile
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? null : TargetVideoStream.Profile;
- }
-
- var targetVideoCodecs = TargetVideoCodec;
- var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
- if (!string.IsNullOrWhiteSpace(videoCodec))
- {
- return GetOption(videoCodec, "profile");
- }
-
- return TargetVideoStream == null ? null : TargetVideoStream.Profile;
- }
- }
-
- /// <summary>
- /// Gets the target video codec tag.
- /// </summary>
- /// <value>The target video codec tag.</value>
- public string TargetVideoCodecTag
- {
- get
- {
- MediaStream stream = TargetVideoStream;
- return !IsDirectStream
- ? null
- : stream == null ? null : stream.CodecTag;
- }
- }
-
- /// <summary>
- /// Predicts the audio bitrate that will be in the output stream
- /// </summary>
- public int? TargetAudioBitrate
- {
- get
- {
- MediaStream stream = TargetAudioStream;
- return AudioBitrate.HasValue && !IsDirectStream
- ? AudioBitrate
- : stream == null ? null : stream.BitRate;
- }
- }
-
- /// <summary>
- /// Predicts the audio channels that will be in the output stream
- /// </summary>
- public int? TargetAudioChannels
- {
- get
- {
- MediaStream stream = TargetAudioStream;
- int? streamChannels = stream == null ? null : stream.Channels;
-
- if (MaxAudioChannels.HasValue && !IsDirectStream)
- {
- if (streamChannels.HasValue)
- {
- return Math.Min(MaxAudioChannels.Value, streamChannels.Value);
- }
-
- return MaxAudioChannels.Value;
- }
-
- return streamChannels;
- }
- }
-
- /// <summary>
- /// Predicts the audio codec that will be in the output stream
- /// </summary>
- public string[] TargetAudioCodec
- {
- get
- {
- MediaStream stream = TargetAudioStream;
-
- string inputCodec = stream == null ? null : stream.Codec;
-
- if (IsDirectStream)
- {
- return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
- }
-
- foreach (string codec in AudioCodecs)
- {
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
- {
- return string.IsNullOrWhiteSpace(codec) ? new string[] { } : new[] { codec };
- }
- }
-
- return AudioCodecs;
- }
- }
-
- public string[] TargetVideoCodec
- {
- get
- {
- MediaStream stream = TargetVideoStream;
-
- string inputCodec = stream == null ? null : stream.Codec;
-
- if (IsDirectStream)
- {
- return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
- }
-
- foreach (string codec in VideoCodecs)
- {
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
- {
- return string.IsNullOrWhiteSpace(codec) ? new string[] { } : new[] { codec };
- }
- }
-
- return VideoCodecs;
- }
- }
-
- /// <summary>
- /// Predicts the audio channels that will be in the output stream
- /// </summary>
- public long? TargetSize
- {
- get
- {
- if (IsDirectStream)
- {
- return MediaSource.Size;
- }
-
- if (RunTimeTicks.HasValue)
- {
- int? totalBitrate = TargetTotalBitrate;
-
- double totalSeconds = RunTimeTicks.Value;
- // Convert to ms
- totalSeconds /= 10000;
- // Convert to seconds
- totalSeconds /= 1000;
-
- return totalBitrate.HasValue ?
- Convert.ToInt64(totalBitrate.Value * totalSeconds) :
- (long?)null;
- }
-
- return null;
- }
- }
-
- public int? TargetVideoBitrate
- {
- get
- {
- MediaStream stream = TargetVideoStream;
-
- return VideoBitrate.HasValue && !IsDirectStream
- ? VideoBitrate
- : stream == null ? null : stream.BitRate;
- }
- }
-
- public TransportStreamTimestamp TargetTimestamp
- {
- get
- {
- TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts")
- ? TransportStreamTimestamp.Valid
- : TransportStreamTimestamp.None;
-
- return !IsDirectStream
- ? defaultValue
- : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
- }
- }
-
- public int? TargetTotalBitrate
- {
- get
- {
- return (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0);
- }
- }
-
- public bool? IsTargetAnamorphic
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
- }
-
- return false;
- }
- }
-
- public bool? IsTargetInterlaced
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
- }
-
- var targetVideoCodecs = TargetVideoCodec;
- var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
- if (!string.IsNullOrWhiteSpace(videoCodec))
- {
- if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
- }
- }
-
- public bool? IsTargetAVC
- {
- get
- {
- if (IsDirectStream)
- {
- return TargetVideoStream == null ? null : TargetVideoStream.IsAVC;
- }
-
- return true;
- }
- }
-
- public int? TargetWidth
- {
- get
- {
- MediaStream videoStream = TargetVideoStream;
-
- if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
- {
- ImageSize size = new ImageSize
- {
- Width = videoStream.Width.Value,
- Height = videoStream.Height.Value
- };
-
- double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
- double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
-
- ImageSize newSize = DrawingUtils.Resize(size,
- null,
- null,
- maxWidth,
- maxHeight);
-
- return Convert.ToInt32(newSize.Width);
- }
-
- return MaxWidth;
- }
- }
-
- public int? TargetHeight
- {
- get
- {
- MediaStream videoStream = TargetVideoStream;
-
- if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
- {
- ImageSize size = new ImageSize
- {
- Width = videoStream.Width.Value,
- Height = videoStream.Height.Value
- };
-
- double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
- double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
-
- ImageSize newSize = DrawingUtils.Resize(size,
- null,
- null,
- maxWidth,
- maxHeight);
-
- return Convert.ToInt32(newSize.Height);
- }
-
- return MaxHeight;
- }
- }
-
- public int? TargetVideoStreamCount
- {
- get
- {
- if (IsDirectStream)
- {
- return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
- }
- return GetMediaStreamCount(MediaStreamType.Video, 1);
- }
- }
-
- public int? TargetAudioStreamCount
- {
- get
- {
- if (IsDirectStream)
- {
- return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
- }
- return GetMediaStreamCount(MediaStreamType.Audio, 1);
- }
- }
-
- private int? GetMediaStreamCount(MediaStreamType type, int limit)
- {
- var count = MediaSource.GetStreamCount(type);
-
- if (count.HasValue)
- {
- count = Math.Min(count.Value, limit);
- }
-
- return count;
- }
-
- public List<MediaStream> GetSelectableAudioStreams()
- {
- return GetSelectableStreams(MediaStreamType.Audio);
- }
-
- public List<MediaStream> GetSelectableSubtitleStreams()
- {
- return GetSelectableStreams(MediaStreamType.Subtitle);
- }
-
- public List<MediaStream> GetSelectableStreams(MediaStreamType type)
- {
- List<MediaStream> list = new List<MediaStream>();
-
- foreach (MediaStream stream in MediaSource.MediaStreams)
- {
- if (type == stream.Type)
- {
- list.Add(stream);
- }
- }
-
- return list;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs
deleted file mode 100644
index e13b32767..000000000
--- a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Session;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class StreamInfoSorter
- {
- public static StreamInfo[] SortMediaSources(List<StreamInfo> streams, long? maxBitrate)
- {
- return streams.OrderBy(i =>
- {
- // Nothing beats direct playing a file
- if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File)
- {
- return 0;
- }
-
- return 1;
-
- }).ThenBy(i =>
- {
- switch (i.PlayMethod)
- {
- // Let's assume direct streaming a file is just as desirable as direct playing a remote url
- case PlayMethod.DirectStream:
- case PlayMethod.DirectPlay:
- return 0;
- default:
- return 1;
- }
-
- }).ThenBy(i =>
- {
- switch (i.MediaSource.Protocol)
- {
- case MediaProtocol.File:
- return 0;
- default:
- return 1;
- }
-
- }).ThenBy(i =>
- {
- if (maxBitrate.HasValue)
- {
- if (i.MediaSource.Bitrate.HasValue)
- {
- return Math.Abs(i.MediaSource.Bitrate.Value - maxBitrate.Value);
- }
- }
-
- return 0;
-
- }).ToArray();
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs b/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs
deleted file mode 100644
index b4e13c5ba..000000000
--- a/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum SubtitleDeliveryMethod
- {
- /// <summary>
- /// The encode
- /// </summary>
- Encode = 0,
- /// <summary>
- /// The embed
- /// </summary>
- Embed = 1,
- /// <summary>
- /// The external
- /// </summary>
- External = 2,
- /// <summary>
- /// The HLS
- /// </summary>
- Hls = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
deleted file mode 100644
index 3f639a520..000000000
--- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class SubtitleProfile
- {
- [XmlAttribute("format")]
- public string Format { get; set; }
-
- [XmlAttribute("method")]
- public SubtitleDeliveryMethod Method { get; set; }
-
- [XmlAttribute("didlMode")]
- public string DidlMode { get; set; }
-
- [XmlAttribute("language")]
- public string Language { get; set; }
-
- public string[] GetLanguages()
- {
- return ContainerProfile.SplitValue(Language);
- }
-
- public bool SupportsLanguage(string subLanguage)
- {
- if (string.IsNullOrEmpty(Language))
- {
- return true;
- }
-
- if (string.IsNullOrEmpty(subLanguage))
- {
- subLanguage = "und";
- }
-
- var languages = GetLanguages();
- return languages.Length == 0 || ListHelper.ContainsIgnoreCase(languages, subLanguage);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
deleted file mode 100644
index 7a89308dc..000000000
--- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public class SubtitleStreamInfo
- {
- public string Url { get; set; }
- public string Language { get; set; }
- public string Name { get; set; }
- public bool IsForced { get; set; }
- public string Format { get; set; }
- public string DisplayTitle { get; set; }
- public int Index { get; set; }
- public SubtitleDeliveryMethod DeliveryMethod { get; set; }
- public bool IsExternalUrl { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
deleted file mode 100644
index 564ce5c60..000000000
--- a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- public enum TranscodeSeekInfo
- {
- Auto = 0,
- Bytes = 1
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
deleted file mode 100644
index 8453fdf6d..000000000
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class TranscodingProfile
- {
- [XmlAttribute("container")]
- public string Container { get; set; }
-
- [XmlAttribute("type")]
- public DlnaProfileType Type { get; set; }
-
- [XmlAttribute("videoCodec")]
- public string VideoCodec { get; set; }
-
- [XmlAttribute("audioCodec")]
- public string AudioCodec { get; set; }
-
- [XmlAttribute("protocol")]
- public string Protocol { get; set; }
-
- [XmlAttribute("estimateContentLength")]
- public bool EstimateContentLength { get; set; }
-
- [XmlAttribute("enableMpegtsM2TsMode")]
- public bool EnableMpegtsM2TsMode { get; set; }
-
- [XmlAttribute("transcodeSeekInfo")]
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-
- [XmlAttribute("copyTimestamps")]
- public bool CopyTimestamps { get; set; }
-
- [XmlAttribute("context")]
- public EncodingContext Context { get; set; }
-
- [XmlAttribute("enableSubtitlesInManifest")]
- public bool EnableSubtitlesInManifest { get; set; }
-
- [XmlAttribute("maxAudioChannels")]
- public string MaxAudioChannels { get; set; }
-
- [XmlAttribute("minSegments")]
- public int MinSegments { get; set; }
-
- [XmlAttribute("segmentLength")]
- public int SegmentLength { get; set; }
-
- [XmlAttribute("breakOnNonKeyFrames")]
- public bool BreakOnNonKeyFrames { get; set; }
-
- public string[] GetAudioCodecs()
- {
- return ContainerProfile.SplitValue(AudioCodec);
- }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
deleted file mode 100644
index f4b9d1e9b..000000000
--- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Net;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class UpnpDeviceInfo
- {
- public Uri Location { get; set; }
- public Dictionary<string, string> Headers { get; set; }
- public IpAddressInfo LocalIpAddress { get; set; }
- public int LocalPort { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs
deleted file mode 100644
index 041d2cd5d..000000000
--- a/MediaBrowser.Model/Dlna/VideoOptions.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Dlna
-{
- /// <summary>
- /// Class VideoOptions.
- /// </summary>
- public class VideoOptions : AudioOptions
- {
- public int? AudioStreamIndex { get; set; }
- public int? SubtitleStreamIndex { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs
deleted file mode 100644
index e8e13ba0d..000000000
--- a/MediaBrowser.Model/Dlna/XmlAttribute.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Xml.Serialization;
-
-namespace MediaBrowser.Model.Dlna
-{
- public class XmlAttribute
- {
- [XmlAttribute("name")]
- public string Name { get; set; }
-
- [XmlAttribute("value")]
- public string Value { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs
deleted file mode 100644
index fcc5ddd16..000000000
--- a/MediaBrowser.Model/Drawing/DrawingUtils.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-namespace MediaBrowser.Model.Drawing
-{
- /// <summary>
- /// Class DrawingUtils
- /// </summary>
- public static class DrawingUtils
- {
- /// <summary>
- /// Resizes a set of dimensions
- /// </summary>
- /// <param name="currentWidth">Width of the current.</param>
- /// <param name="currentHeight">Height of the current.</param>
- /// <param name="scaleFactor">The scale factor.</param>
- /// <returns>ImageSize.</returns>
- public static ImageSize Scale(double currentWidth, double currentHeight, double scaleFactor)
- {
- return Scale(new ImageSize
- {
- Width = currentWidth,
- Height = currentHeight
-
- }, scaleFactor);
- }
-
- /// <summary>
- /// Resizes a set of dimensions
- /// </summary>
- /// <param name="size">The size.</param>
- /// <param name="scaleFactor">The scale factor.</param>
- /// <returns>ImageSize.</returns>
- public static ImageSize Scale(ImageSize size, double scaleFactor)
- {
- double newWidth = size.Width * scaleFactor;
-
- return Resize(size.Width, size.Height, newWidth, null, null, null);
- }
-
- /// <summary>
- /// Resizes a set of dimensions
- /// </summary>
- /// <param name="currentWidth">Width of the current.</param>
- /// <param name="currentHeight">Height of the current.</param>
- /// <param name="width">The width.</param>
- /// <param name="height">The height.</param>
- /// <param name="maxWidth">A max fixed width, if desired</param>
- /// <param name="maxHeight">A max fixed height, if desired</param>
- /// <returns>ImageSize.</returns>
- public static ImageSize Resize(double currentWidth,
- double currentHeight,
- double? width,
- double? height,
- double? maxWidth,
- double? maxHeight)
- {
- return Resize(new ImageSize
- {
- Width = currentWidth,
- Height = currentHeight
-
- }, width, height, maxWidth, maxHeight);
- }
-
- /// <summary>
- /// Resizes a set of dimensions
- /// </summary>
- /// <param name="size">The original size object</param>
- /// <param name="width">A new fixed width, if desired</param>
- /// <param name="height">A new fixed height, if desired</param>
- /// <param name="maxWidth">A max fixed width, if desired</param>
- /// <param name="maxHeight">A max fixed height, if desired</param>
- /// <returns>A new size object</returns>
- public static ImageSize Resize(ImageSize size,
- double? width,
- double? height,
- double? maxWidth,
- double? maxHeight)
- {
- double newWidth = size.Width;
- double newHeight = size.Height;
-
- if (width.HasValue && height.HasValue)
- {
- newWidth = width.Value;
- newHeight = height.Value;
- }
-
- else if (height.HasValue)
- {
- newWidth = GetNewWidth(newHeight, newWidth, height.Value);
- newHeight = height.Value;
- }
-
- else if (width.HasValue)
- {
- newHeight = GetNewHeight(newHeight, newWidth, width.Value);
- newWidth = width.Value;
- }
-
- if (maxHeight.HasValue && maxHeight.Value < newHeight)
- {
- newWidth = GetNewWidth(newHeight, newWidth, maxHeight.Value);
- newHeight = maxHeight.Value;
- }
-
- if (maxWidth.HasValue && maxWidth.Value < newWidth)
- {
- newHeight = GetNewHeight(newHeight, newWidth, maxWidth.Value);
- newWidth = maxWidth.Value;
- }
-
- return new ImageSize { Width = newWidth, Height = newHeight };
- }
-
- /// <summary>
- /// Gets the new width.
- /// </summary>
- /// <param name="currentHeight">Height of the current.</param>
- /// <param name="currentWidth">Width of the current.</param>
- /// <param name="newHeight">The new height.</param>
- /// <returns>System.Double.</returns>
- private static double GetNewWidth(double currentHeight, double currentWidth, double newHeight)
- {
- double scaleFactor = newHeight;
- scaleFactor /= currentHeight;
- scaleFactor *= currentWidth;
-
- return scaleFactor;
- }
-
- /// <summary>
- /// Gets the new height.
- /// </summary>
- /// <param name="currentHeight">Height of the current.</param>
- /// <param name="currentWidth">Width of the current.</param>
- /// <param name="newWidth">The new width.</param>
- /// <returns>System.Double.</returns>
- private static double GetNewHeight(double currentHeight, double currentWidth, double newWidth)
- {
- double scaleFactor = newWidth;
- scaleFactor /= currentWidth;
- scaleFactor *= currentHeight;
-
- return scaleFactor;
- }
- }
-}
diff --git a/MediaBrowser.Model/Drawing/ImageFormat.cs b/MediaBrowser.Model/Drawing/ImageFormat.cs
deleted file mode 100644
index 0172c9754..000000000
--- a/MediaBrowser.Model/Drawing/ImageFormat.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Drawing
-{
- /// <summary>
- /// Enum ImageOutputFormat
- /// </summary>
- public enum ImageFormat
- {
- /// <summary>
- /// The BMP
- /// </summary>
- Bmp,
- /// <summary>
- /// The GIF
- /// </summary>
- Gif,
- /// <summary>
- /// The JPG
- /// </summary>
- Jpg,
- /// <summary>
- /// The PNG
- /// </summary>
- Png,
- /// <summary>
- /// The webp
- /// </summary>
- Webp
- }
-}
diff --git a/MediaBrowser.Model/Drawing/ImageOrientation.cs b/MediaBrowser.Model/Drawing/ImageOrientation.cs
deleted file mode 100644
index c320a8224..000000000
--- a/MediaBrowser.Model/Drawing/ImageOrientation.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Model.Drawing
-{
- public enum ImageOrientation
- {
- TopLeft = 1,
- TopRight = 2,
- BottomRight = 3,
- BottomLeft = 4,
- LeftTop = 5,
- RightTop = 6,
- RightBottom = 7,
- LeftBottom = 8,
- }
-}
diff --git a/MediaBrowser.Model/Drawing/ImageSize.cs b/MediaBrowser.Model/Drawing/ImageSize.cs
deleted file mode 100644
index c2b0291bd..000000000
--- a/MediaBrowser.Model/Drawing/ImageSize.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System.Globalization;
-
-namespace MediaBrowser.Model.Drawing
-{
- /// <summary>
- /// Struct ImageSize
- /// </summary>
- public struct ImageSize
- {
- private double _height;
- private double _width;
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public double Height
- {
- get
- {
- return _height;
- }
- set
- {
- _height = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public double Width
- {
- get { return _width; }
- set { _width = value; }
- }
-
- public bool Equals(ImageSize size)
- {
- return Width.Equals(size.Width) && Height.Equals(size.Height);
- }
-
- public override string ToString()
- {
- return string.Format("{0}-{1}", Width, Height);
- }
-
- public ImageSize(string value)
- {
- _width = 0;
-
- _height = 0;
-
- ParseValue(value);
- }
-
- public ImageSize(int width, int height)
- {
- _width = width;
- _height = height;
- }
-
- public ImageSize(double width, double height)
- {
- _width = width;
- _height = height;
- }
-
- private void ParseValue(string value)
- {
- if (!string.IsNullOrEmpty(value))
- {
- string[] parts = value.Split('-');
-
- if (parts.Length == 2)
- {
- double val;
-
- if (double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out val))
- {
- _width = val;
- }
-
- if (double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out val))
- {
- _height = val;
- }
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
deleted file mode 100644
index ba975db44..000000000
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ /dev/null
@@ -1,824 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// This is strictly used as a data transfer object from the api layer.
- /// This holds information about a BaseItem in a format that is convenient for the client.
- /// </summary>
- [DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
- public class BaseItemDto : IHasProviderIds, IItemDto, IHasServerId, IHasSyncInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- public string OriginalTitle { get; set; }
-
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the etag.
- /// </summary>
- /// <value>The etag.</value>
- public string Etag { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the source.
- /// </summary>
- /// <value>The type of the source.</value>
- public string SourceType { get; set; }
-
- /// <summary>
- /// Gets or sets the playlist item identifier.
- /// </summary>
- /// <value>The playlist item identifier.</value>
- public string PlaylistItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- public DateTime? DateCreated { get; set; }
-
- public DateTime? DateLastMediaAdded { get; set; }
- public string ExtraType { get; set; }
-
- public int? AirsBeforeSeasonNumber { get; set; }
- public int? AirsAfterSeasonNumber { get; set; }
- public int? AirsBeforeEpisodeNumber { get; set; }
- public int? AbsoluteEpisodeNumber { get; set; }
- public bool? DisplaySpecialsWithSeasons { get; set; }
- public bool? CanDelete { get; set; }
- public bool? CanDownload { get; set; }
-
- public bool? HasSubtitles { get; set; }
-
- public string PreferredMetadataLanguage { get; set; }
- public string PreferredMetadataCountryCode { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports synchronize].
- /// </summary>
- /// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value>
- public bool? SupportsSync { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance has synchronize job.
- /// </summary>
- /// <value><c>null</c> if [has synchronize job] contains no value, <c>true</c> if [has synchronize job]; otherwise, <c>false</c>.</value>
- public bool? HasSyncJob { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is synced.
- /// </summary>
- /// <value><c>null</c> if [is synced] contains no value, <c>true</c> if [is synced]; otherwise, <c>false</c>.</value>
- public bool? IsSynced { get; set; }
- /// <summary>
- /// Gets or sets the synchronize status.
- /// </summary>
- /// <value>The synchronize status.</value>
- public SyncJobItemStatus? SyncStatus { get; set; }
- /// <summary>
- /// Gets or sets the synchronize percent.
- /// </summary>
- /// <value>The synchronize percent.</value>
- public double? SyncPercent { get; set; }
-
- public string Container { get; set; }
-
- /// <summary>
- /// Gets or sets the DVD season number.
- /// </summary>
- /// <value>The DVD season number.</value>
- public int? DvdSeasonNumber { get; set; }
- /// <summary>
- /// Gets or sets the DVD episode number.
- /// </summary>
- /// <value>The DVD episode number.</value>
- public float? DvdEpisodeNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the sort.
- /// </summary>
- /// <value>The name of the sort.</value>
- public string SortName { get; set; }
- public string ForcedSortName { get; set; }
-
- /// <summary>
- /// Gets or sets the video3 D format.
- /// </summary>
- /// <value>The video3 D format.</value>
- public Video3DFormat? Video3DFormat { get; set; }
-
- /// <summary>
- /// Gets or sets the premiere date.
- /// </summary>
- /// <value>The premiere date.</value>
- public DateTime? PremiereDate { get; set; }
-
- /// <summary>
- /// Gets or sets the external urls.
- /// </summary>
- /// <value>The external urls.</value>
- public ExternalUrl[] ExternalUrls { get; set; }
-
- /// <summary>
- /// Gets or sets the media versions.
- /// </summary>
- /// <value>The media versions.</value>
- public List<MediaSourceInfo> MediaSources { get; set; }
-
- /// <summary>
- /// Gets or sets the critic rating.
- /// </summary>
- /// <value>The critic rating.</value>
- public float? CriticRating { get; set; }
-
- /// <summary>
- /// Gets or sets the game system.
- /// </summary>
- /// <value>The game system.</value>
- public string GameSystem { get; set; }
-
- public string[] ProductionLocations { get; set; }
-
- public string[] MultiPartGameFiles { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- public string OfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the custom rating.
- /// </summary>
- /// <value>The custom rating.</value>
- public string CustomRating { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
- public string ChannelName { get; set; }
- public string ServiceName { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
-
- /// <summary>
- /// Gets or sets the taglines.
- /// </summary>
- /// <value>The taglines.</value>
- public string[] Taglines { get; set; }
-
- /// <summary>
- /// Gets or sets the genres.
- /// </summary>
- /// <value>The genres.</value>
- public List<string> Genres { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- public float? CommunityRating { get; set; }
-
- /// <summary>
- /// Gets or sets the cumulative run time ticks.
- /// </summary>
- /// <value>The cumulative run time ticks.</value>
- public long? CumulativeRunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- public long? RunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the play access.
- /// </summary>
- /// <value>The play access.</value>
- public PlayAccess? PlayAccess { get; set; }
-
- /// <summary>
- /// Gets or sets the aspect ratio.
- /// </summary>
- /// <value>The aspect ratio.</value>
- public string AspectRatio { get; set; }
-
- /// <summary>
- /// Gets or sets the production year.
- /// </summary>
- /// <value>The production year.</value>
- public int? ProductionYear { 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>
- public bool? IsPlaceHolder { get; set; }
-
- /// <summary>
- /// Gets or sets the number.
- /// </summary>
- /// <value>The number.</value>
- public string Number { get; set; }
- public string ChannelNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the index number.
- /// </summary>
- /// <value>The index number.</value>
- public int? IndexNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the index number end.
- /// </summary>
- /// <value>The index number end.</value>
- public int? IndexNumberEnd { 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 trailer urls.
- /// </summary>
- /// <value>The trailer urls.</value>
- public MediaUrl[] RemoteTrailers { get; set; }
-
- /// <summary>
- /// Gets or sets the provider ids.
- /// </summary>
- /// <value>The provider ids.</value>
- public Dictionary<string, string> ProviderIds { get; set; }
-
- /// <summary>
- /// Gets or sets 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 a value indicating whether this instance is folder.
- /// </summary>
- /// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
- public bool? IsFolder { get; set; }
-
- /// <summary>
- /// Gets or sets the parent id.
- /// </summary>
- /// <value>The parent id.</value>
- public string ParentId { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the people.
- /// </summary>
- /// <value>The people.</value>
- public BaseItemPerson[] People { get; set; }
-
- /// <summary>
- /// Gets or sets the studios.
- /// </summary>
- /// <value>The studios.</value>
- public NameIdPair[] Studios { get; set; }
-
- public NameIdPair[] GenreItems { get; set; }
-
- /// <summary>
- /// If the item does not have a logo, this will hold the Id of the Parent that has one.
- /// </summary>
- /// <value>The parent logo item id.</value>
- public string ParentLogoItemId { get; set; }
-
- /// <summary>
- /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
- /// </summary>
- /// <value>The parent backdrop item id.</value>
- public string ParentBackdropItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent backdrop image tags.
- /// </summary>
- /// <value>The parent backdrop image tags.</value>
- public string[] ParentBackdropImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the local trailer count.
- /// </summary>
- /// <value>The local trailer count.</value>
- public int? LocalTrailerCount { get; set; }
-
- /// <summary>
- /// User data for this item based on the user it's being requested for
- /// </summary>
- /// <value>The user data.</value>
- public UserItemDataDto UserData { get; set; }
-
- /// <summary>
- /// Gets or sets the recursive item count.
- /// </summary>
- /// <value>The recursive item count.</value>
- public int? RecursiveItemCount { get; set; }
-
- /// <summary>
- /// Gets or sets the child count.
- /// </summary>
- /// <value>The child count.</value>
- public int? ChildCount { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the series.
- /// </summary>
- /// <value>The name of the series.</value>
- public string SeriesName { get; set; }
-
- /// <summary>
- /// Gets or sets the series id.
- /// </summary>
- /// <value>The series id.</value>
- public string SeriesId { get; set; }
-
- /// <summary>
- /// Gets or sets the season identifier.
- /// </summary>
- /// <value>The season identifier.</value>
- public string SeasonId { get; set; }
-
- /// <summary>
- /// Gets or sets the special feature count.
- /// </summary>
- /// <value>The special feature count.</value>
- public int? SpecialFeatureCount { get; set; }
-
- /// <summary>
- /// Gets or sets the display preferences id.
- /// </summary>
- /// <value>The display preferences id.</value>
- public string DisplayPreferencesId { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public string Status { 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 air days.
- /// </summary>
- /// <value>The air days.</value>
- public DayOfWeek[] AirDays { get; set; }
-
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- public string[] Tags { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image aspect ratio, after image enhancements.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- public double? PrimaryImageAspectRatio { get; set; }
-
- /// <summary>
- /// Gets or sets the artists.
- /// </summary>
- /// <value>The artists.</value>
- public string[] Artists { get; set; }
-
- /// <summary>
- /// Gets or sets the artist items.
- /// </summary>
- /// <value>The artist items.</value>
- public NameIdPair[] ArtistItems { get; set; }
-
- /// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- public string Album { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the collection.
- /// </summary>
- /// <value>The type of the collection.</value>
- public string CollectionType { get; set; }
-
- /// <summary>
- /// Gets or sets the display order.
- /// </summary>
- /// <value>The display order.</value>
- public string DisplayOrder { get; set; }
-
- /// <summary>
- /// Gets or sets the album id.
- /// </summary>
- /// <value>The album id.</value>
- public string AlbumId { get; set; }
- /// <summary>
- /// Gets or sets the album image tag.
- /// </summary>
- /// <value>The album image tag.</value>
- public string AlbumPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the series primary image tag.
- /// </summary>
- /// <value>The series primary image tag.</value>
- public string SeriesPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the album artist.
- /// </summary>
- /// <value>The album artist.</value>
- public string AlbumArtist { get; set; }
-
- /// <summary>
- /// Gets or sets the album artists.
- /// </summary>
- /// <value>The album artists.</value>
- public NameIdPair[] AlbumArtists { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the season.
- /// </summary>
- /// <value>The name of the season.</value>
- public string SeasonName { get; set; }
-
- /// <summary>
- /// Gets or sets the media streams.
- /// </summary>
- /// <value>The media streams.</value>
- public MediaStream[] MediaStreams { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the video.
- /// </summary>
- /// <value>The type of the video.</value>
- public VideoType? VideoType { get; set; }
-
- /// <summary>
- /// Gets or sets the part count.
- /// </summary>
- /// <value>The part count.</value>
- public int? PartCount { get; set; }
- public int? MediaSourceCount { get; set; }
-
- /// <summary>
- /// Determines whether the specified type is type.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns><c>true</c> if the specified type is type; otherwise, <c>false</c>.</returns>
- public bool IsType(Type type)
- {
- return IsType(type.Name);
- }
-
- /// <summary>
- /// Determines whether the specified type is type.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns><c>true</c> if the specified type is type; otherwise, <c>false</c>.</returns>
- public bool IsType(string type)
- {
- return StringHelper.EqualsIgnoreCase(Type, type);
- }
-
- /// <summary>
- /// Gets or sets the image tags.
- /// </summary>
- /// <value>The image tags.</value>
- public Dictionary<ImageType, string> ImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the backdrop image tags.
- /// </summary>
- /// <value>The backdrop image tags.</value>
- public string[] BackdropImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the screenshot image tags.
- /// </summary>
- /// <value>The screenshot image tags.</value>
- public string[] ScreenshotImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the parent logo image tag.
- /// </summary>
- /// <value>The parent logo image tag.</value>
- public string ParentLogoImageTag { get; set; }
-
- /// <summary>
- /// If the item does not have a art, this will hold the Id of the Parent that has one.
- /// </summary>
- /// <value>The parent art item id.</value>
- public string ParentArtItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent art image tag.
- /// </summary>
- /// <value>The parent art image tag.</value>
- public string ParentArtImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the series thumb image tag.
- /// </summary>
- /// <value>The series thumb image tag.</value>
- public string SeriesThumbImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the series studio.
- /// </summary>
- /// <value>The series studio.</value>
- public string SeriesStudio { get; set; }
-
- /// <summary>
- /// Gets or sets the parent thumb item id.
- /// </summary>
- /// <value>The parent thumb item id.</value>
- public string ParentThumbItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent thumb image tag.
- /// </summary>
- /// <value>The parent thumb image tag.</value>
- public string ParentThumbImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the parent primary image item identifier.
- /// </summary>
- /// <value>The parent primary image item identifier.</value>
- public string ParentPrimaryImageItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent primary image tag.
- /// </summary>
- /// <value>The parent primary image tag.</value>
- public string ParentPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the chapters.
- /// </summary>
- /// <value>The chapters.</value>
- public List<ChapterInfoDto> Chapters { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the location.
- /// </summary>
- /// <value>The type of the location.</value>
- public LocationType? LocationType { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the iso.
- /// </summary>
- /// <value>The type of the iso.</value>
- public IsoType? IsoType { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- public string MediaType { get; set; }
-
- /// <summary>
- /// Gets or sets the end date.
- /// </summary>
- /// <value>The end date.</value>
- public DateTime? EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- public string HomePageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the locked fields.
- /// </summary>
- /// <value>The locked fields.</value>
- public MetadataFields[] LockedFields { get; set; }
-
- /// <summary>
- /// Gets or sets the trailer count.
- /// </summary>
- /// <value>The trailer count.</value>
- public int? TrailerCount { get; set; }
- /// <summary>
- /// Gets or sets the movie count.
- /// </summary>
- /// <value>The movie count.</value>
- public int? MovieCount { get; set; }
- /// <summary>
- /// Gets or sets the series count.
- /// </summary>
- /// <value>The series count.</value>
- public int? SeriesCount { get; set; }
- public int? ProgramCount { get; set; }
- /// <summary>
- /// Gets or sets the episode count.
- /// </summary>
- /// <value>The episode count.</value>
- public int? EpisodeCount { get; set; }
- /// <summary>
- /// Gets or sets the game count.
- /// </summary>
- /// <value>The game count.</value>
- public int? GameCount { get; set; }
- /// <summary>
- /// Gets or sets the song count.
- /// </summary>
- /// <value>The song count.</value>
- public int? SongCount { get; set; }
- /// <summary>
- /// Gets or sets the album count.
- /// </summary>
- /// <value>The album count.</value>
- public int? AlbumCount { get; set; }
- public int? ArtistCount { get; set; }
- /// <summary>
- /// Gets or sets the music video count.
- /// </summary>
- /// <value>The music video count.</value>
- public int? MusicVideoCount { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable internet providers].
- /// </summary>
- /// <value><c>true</c> if [enable internet providers]; otherwise, <c>false</c>.</value>
- public bool? LockData { get; set; }
-
- public int? Width { get; set; }
- public int? Height { get; set; }
- public string CameraMake { get; set; }
- public string CameraModel { get; set; }
- public string Software { get; set; }
- public double? ExposureTime { get; set; }
- public double? FocalLength { get; set; }
- public ImageOrientation? ImageOrientation { get; set; }
- public double? Aperture { get; set; }
- public double? ShutterSpeed { get; set; }
- public double? Latitude { get; set; }
- public double? Longitude { get; set; }
- public double? Altitude { get; set; }
- public int? IsoSpeedRating { get; set; }
-
- /// <summary>
- /// Used by RecordingGroup
- /// </summary>
- public int? RecordingCount { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- /// <summary>
- /// Gets or sets the channel primary image tag.
- /// </summary>
- /// <value>The channel primary image tag.</value>
- public string ChannelPrimaryImageTag { get; set; }
-
- /// <summary>
- /// The start date of the recording, in UTC.
- /// </summary>
- public DateTime? StartDate { get; set; }
-
- /// <summary>
- /// Gets or sets the completion percentage.
- /// </summary>
- /// <value>The completion percentage.</value>
- public double? CompletionPercentage { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is repeat.
- /// </summary>
- /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
- public bool? IsRepeat { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- public string EpisodeTitle { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType? ChannelType { get; set; }
-
- /// <summary>
- /// Gets or sets the audio.
- /// </summary>
- /// <value>The audio.</value>
- public ProgramAudio? Audio { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
- public bool? IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- public bool? IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- public bool? IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- public bool? IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- public bool? IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- public bool? IsKids { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premiere.
- /// </summary>
- /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
- public bool? IsPremiere { get; set; }
-
- /// <summary>
- /// Gets or sets the timer identifier.
- /// </summary>
- /// <value>The timer identifier.</value>
- public string TimerId { get; set; }
- /// <summary>
- /// Gets or sets the current program.
- /// </summary>
- /// <value>The current program.</value>
- public BaseItemDto CurrentProgram { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
deleted file mode 100644
index e73872cb7..000000000
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// This is used by the api to get information about a Person within a BaseItem
- /// </summary>
- [DebuggerDisplay("Name = {Name}, Role = {Role}, Type = {Type}")]
- public class BaseItemPerson
- {
- /// <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 role.
- /// </summary>
- /// <value>The role.</value>
- public string Role { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image tag.
- /// </summary>
- /// <value>The primary image tag.</value>
- public string PrimaryImageTag { get; set; }
-
- /// <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 PrimaryImageTag != null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ChapterInfoDto.cs b/MediaBrowser.Model/Dto/ChapterInfoDto.cs
deleted file mode 100644
index 51e0a545a..000000000
--- a/MediaBrowser.Model/Dto/ChapterInfoDto.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ChapterInfo
- /// </summary>
- [DebuggerDisplay("Name = {Name}")]
- public class ChapterInfoDto
- {
- /// <summary>
- /// Gets or sets the start position ticks.
- /// </summary>
- /// <value>The start position ticks.</value>
- public long StartPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the image tag.
- /// </summary>
- /// <value>The image tag.</value>
- public string ImageTag { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance has image.
- /// </summary>
- /// <value><c>true</c> if this instance has image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasImage
- {
- get { return ImageTag != null; }
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/GameSystemSummary.cs b/MediaBrowser.Model/Dto/GameSystemSummary.cs
deleted file mode 100644
index 2cd9d408d..000000000
--- a/MediaBrowser.Model/Dto/GameSystemSummary.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class GameSystemSummary
- /// </summary>
- public class GameSystemSummary
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string DisplayName { get; set; }
-
- /// <summary>
- /// Gets or sets the game count.
- /// </summary>
- /// <value>The game count.</value>
- public int GameCount { get; set; }
-
- /// <summary>
- /// Gets or sets the game extensions.
- /// </summary>
- /// <value>The game extensions.</value>
- public string[] GameFileExtensions { get; set; }
-
- /// <summary>
- /// Gets or sets the client installed game count.
- /// </summary>
- /// <value>The client installed game count.</value>
- public int ClientInstalledGameCount { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GameSystemSummary"/> class.
- /// </summary>
- public GameSystemSummary()
- {
- GameFileExtensions = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/IHasServerId.cs b/MediaBrowser.Model/Dto/IHasServerId.cs
deleted file mode 100644
index 0515203da..000000000
--- a/MediaBrowser.Model/Dto/IHasServerId.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- public interface IHasServerId
- {
- string ServerId { get; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/IHasSyncInfo.cs b/MediaBrowser.Model/Dto/IHasSyncInfo.cs
deleted file mode 100644
index 7471dacdd..000000000
--- a/MediaBrowser.Model/Dto/IHasSyncInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Model.Dto
-{
- public interface IHasSyncInfo
- {
- string Id { get; }
- bool? SupportsSync { get; set; }
- bool? HasSyncJob { get; set; }
- double? SyncPercent { get; set; }
- bool? IsSynced { get; set; }
- SyncJobItemStatus? SyncStatus { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/IItemDto.cs b/MediaBrowser.Model/Dto/IItemDto.cs
deleted file mode 100644
index 3e7d1c608..000000000
--- a/MediaBrowser.Model/Dto/IItemDto.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Interface IItemDto
- /// </summary>
- public interface IItemDto
- {
- /// <summary>
- /// Gets or sets the primary image aspect ratio.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- double? PrimaryImageAspectRatio { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ImageByNameInfo.cs b/MediaBrowser.Model/Dto/ImageByNameInfo.cs
deleted file mode 100644
index b7921d993..000000000
--- a/MediaBrowser.Model/Dto/ImageByNameInfo.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- public class ImageByNameInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the theme.
- /// </summary>
- /// <value>The theme.</value>
- public string Theme { get; set; }
- /// <summary>
- /// Gets or sets the context.
- /// </summary>
- /// <value>The context.</value>
- public string Context { get; set; }
- /// <summary>
- /// Gets or sets the length of the file.
- /// </summary>
- /// <value>The length of the file.</value>
- public long FileLength { get; set; }
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- public string Format { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ImageInfo.cs b/MediaBrowser.Model/Dto/ImageInfo.cs
deleted file mode 100644
index 5eabb16a5..000000000
--- a/MediaBrowser.Model/Dto/ImageInfo.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ImageInfo
- /// </summary>
- public class ImageInfo
- {
- /// <summary>
- /// Gets or sets the type of the image.
- /// </summary>
- /// <value>The type of the image.</value>
- public ImageType ImageType { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the image.
- /// </summary>
- /// <value>The index of the image.</value>
- public int? ImageIndex { get; set; }
-
- /// <summary>
- /// The image tag
- /// </summary>
- public string ImageTag;
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the size.
- /// </summary>
- /// <value>The size.</value>
- public long Size { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs
deleted file mode 100644
index 98bd0279a..000000000
--- a/MediaBrowser.Model/Dto/ImageOptions.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ImageOptions
- /// </summary>
- public class ImageOptions
- {
- /// <summary>
- /// Gets or sets the type of the image.
- /// </summary>
- /// <value>The type of the image.</value>
- public ImageType ImageType { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the image.
- /// </summary>
- /// <value>The index of the image.</value>
- public int? ImageIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width of the max.
- /// </summary>
- /// <value>The width of the max.</value>
- public int? MaxWidth { get; set; }
-
- /// <summary>
- /// Gets or sets the height of the max.
- /// </summary>
- /// <value>The height of the max.</value>
- public int? MaxHeight { get; set; }
-
- /// <summary>
- /// Gets or sets the quality.
- /// </summary>
- /// <value>The quality.</value>
- public int? Quality { get; set; }
-
- /// <summary>
- /// Gets or sets the image tag.
- /// If set this will result in strong, unconditional response caching
- /// </summary>
- /// <value>The hash.</value>
- public string Tag { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [crop whitespace].
- /// </summary>
- /// <value><c>null</c> if [crop whitespace] contains no value, <c>true</c> if [crop whitespace]; otherwise, <c>false</c>.</value>
- public bool? CropWhitespace { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable image enhancers].
- /// </summary>
- /// <value><c>true</c> if [enable image enhancers]; otherwise, <c>false</c>.</value>
- public bool EnableImageEnhancers { get; set; }
-
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- public ImageFormat? Format { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [add played indicator].
- /// </summary>
- /// <value><c>true</c> if [add played indicator]; otherwise, <c>false</c>.</value>
- public bool AddPlayedIndicator { get; set; }
-
- /// <summary>
- /// Gets or sets the percent played.
- /// </summary>
- /// <value>The percent played.</value>
- public int? PercentPlayed { get; set; }
-
- /// <summary>
- /// Gets or sets the un played count.
- /// </summary>
- /// <value>The un played count.</value>
- public int? UnPlayedCount { get; set; }
-
- /// <summary>
- /// Gets or sets the color of the background.
- /// </summary>
- /// <value>The color of the background.</value>
- public string BackgroundColor { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ImageOptions" /> class.
- /// </summary>
- public ImageOptions()
- {
- EnableImageEnhancers = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ItemCounts.cs b/MediaBrowser.Model/Dto/ItemCounts.cs
deleted file mode 100644
index 8ceb3a86b..000000000
--- a/MediaBrowser.Model/Dto/ItemCounts.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class LibrarySummary
- /// </summary>
- public class ItemCounts
- {
- /// <summary>
- /// Gets or sets the movie count.
- /// </summary>
- /// <value>The movie count.</value>
- public int MovieCount { get; set; }
- /// <summary>
- /// Gets or sets the series count.
- /// </summary>
- /// <value>The series count.</value>
- public int SeriesCount { get; set; }
- /// <summary>
- /// Gets or sets the episode count.
- /// </summary>
- /// <value>The episode count.</value>
- public int EpisodeCount { get; set; }
- /// <summary>
- /// Gets or sets the game count.
- /// </summary>
- /// <value>The game count.</value>
- public int GameCount { get; set; }
- public int ArtistCount { get; set; }
- public int ProgramCount { get; set; }
- /// <summary>
- /// Gets or sets the game system count.
- /// </summary>
- /// <value>The game system count.</value>
- public int GameSystemCount { get; set; }
- /// <summary>
- /// Gets or sets the trailer count.
- /// </summary>
- /// <value>The trailer count.</value>
- public int TrailerCount { get; set; }
- /// <summary>
- /// Gets or sets the song count.
- /// </summary>
- /// <value>The song count.</value>
- public int SongCount { get; set; }
- /// <summary>
- /// Gets or sets the album count.
- /// </summary>
- /// <value>The album count.</value>
- public int AlbumCount { get; set; }
- /// <summary>
- /// Gets or sets the music video count.
- /// </summary>
- /// <value>The music video count.</value>
- public int MusicVideoCount { get; set; }
- /// <summary>
- /// Gets or sets the box set count.
- /// </summary>
- /// <value>The box set count.</value>
- public int BoxSetCount { get; set; }
- /// <summary>
- /// Gets or sets the book count.
- /// </summary>
- /// <value>The book count.</value>
- public int BookCount { get; set; }
- public int ItemCount { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/ItemIndex.cs b/MediaBrowser.Model/Dto/ItemIndex.cs
deleted file mode 100644
index 96cef622b..000000000
--- a/MediaBrowser.Model/Dto/ItemIndex.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ItemIndex
- /// </summary>
- public class ItemIndex
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the item count.
- /// </summary>
- /// <value>The item count.</value>
- public int ItemCount { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
deleted file mode 100644
index d6301b331..000000000
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ /dev/null
@@ -1,228 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.MediaInfo;
-using System.Collections.Generic;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Model.Dto
-{
- public class MediaSourceInfo
- {
- public MediaProtocol Protocol { get; set; }
- public string Id { get; set; }
-
- public string Path { get; set; }
-
- public MediaSourceType Type { get; set; }
-
- public string Container { get; set; }
- public long? Size { get; set; }
-
- public string Name { get; set; }
-
- /// <summary>
- /// Differentiate internet url vs local network
- /// </summary>
- public bool IsRemote { get; set; }
-
- public string ETag { get; set; }
- public long? RunTimeTicks { get; set; }
- public bool ReadAtNativeFramerate { get; set; }
- public bool IgnoreDts { get; set; }
- public bool IgnoreIndex { get; set; }
- public bool GenPtsInput { get; set; }
- public bool SupportsTranscoding { get; set; }
- public bool SupportsDirectStream { get; set; }
- public bool SupportsDirectPlay { get; set; }
- public bool IsInfiniteStream { get; set; }
- public bool RequiresOpening { get; set; }
- public string OpenToken { get; set; }
- public bool RequiresClosing { get; set; }
- public bool SupportsProbing { get; set; }
- public string LiveStreamId { get; set; }
- public int? BufferMs { get; set; }
-
- public bool RequiresLooping { get; set; }
-
- public VideoType? VideoType { get; set; }
-
- public IsoType? IsoType { get; set; }
-
- public Video3DFormat? Video3DFormat { get; set; }
-
- public List<MediaStream> MediaStreams { get; set; }
-
- public string[] Formats { get; set; }
-
- public int? Bitrate { get; set; }
-
- public TransportStreamTimestamp? Timestamp { get; set; }
- public Dictionary<string, string> RequiredHttpHeaders { get; set; }
-
- public string TranscodingUrl { get; set; }
- public string TranscodingSubProtocol { get; set; }
- public string TranscodingContainer { get; set; }
-
- public int? AnalyzeDurationMs { get; set; }
-
- public MediaSourceInfo()
- {
- Formats = new string[] { };
- MediaStreams = new List<MediaStream>();
- RequiredHttpHeaders = new Dictionary<string, string>();
- SupportsTranscoding = true;
- SupportsDirectStream = true;
- SupportsDirectPlay = true;
- SupportsProbing = true;
- }
-
- public void InferTotalBitrate(bool force = false)
- {
- if (MediaStreams == null)
- {
- return;
- }
-
- if (!force && Bitrate.HasValue)
- {
- return;
- }
-
- var bitrate = 0;
- foreach (var stream in MediaStreams)
- {
- if (!stream.IsExternal)
- {
- bitrate += stream.BitRate ?? 0;
- }
- }
-
- if (bitrate > 0)
- {
- Bitrate = bitrate;
- }
- }
-
- [IgnoreDataMember]
- public List<TranscodeReason> TranscodeReasons { get; set; }
-
- public int? DefaultAudioStreamIndex { get; set; }
- public int? DefaultSubtitleStreamIndex { get; set; }
-
- [IgnoreDataMember]
- public MediaStream DefaultAudioStream
- {
- get { return GetDefaultAudioStream(DefaultAudioStreamIndex); }
- }
-
- public MediaStream GetDefaultAudioStream(int? defaultIndex)
- {
- if (defaultIndex.HasValue)
- {
- var val = defaultIndex.Value;
-
- foreach (MediaStream i in MediaStreams)
- {
- if (i.Type == MediaStreamType.Audio && i.Index == val)
- {
- return i;
- }
- }
- }
-
- foreach (MediaStream i in MediaStreams)
- {
- if (i.Type == MediaStreamType.Audio && i.IsDefault)
- {
- return i;
- }
- }
-
- foreach (MediaStream i in MediaStreams)
- {
- if (i.Type == MediaStreamType.Audio)
- {
- return i;
- }
- }
-
- return null;
- }
-
- [IgnoreDataMember]
- public MediaStream VideoStream
- {
- get
- {
- foreach (MediaStream i in MediaStreams)
- {
- if (i.Type == MediaStreamType.Video)
- {
- return i;
- }
- }
-
- return null;
- }
- }
-
- public MediaStream GetMediaStream(MediaStreamType type, int index)
- {
- foreach (MediaStream i in MediaStreams)
- {
- if (i.Type == type && i.Index == index)
- {
- return i;
- }
- }
-
- return null;
- }
-
- public int? GetStreamCount(MediaStreamType type)
- {
- int numMatches = 0;
- int numStreams = 0;
-
- foreach (MediaStream i in MediaStreams)
- {
- numStreams++;
- if (i.Type == type)
- {
- numMatches++;
- }
- }
-
- if (numStreams == 0)
- {
- return null;
- }
-
- return numMatches;
- }
-
- public bool? IsSecondaryAudio(MediaStream stream)
- {
- // Look for the first audio track marked as default
- foreach (MediaStream currentStream in MediaStreams)
- {
- if (currentStream.Type == MediaStreamType.Audio && currentStream.IsDefault)
- {
- return currentStream.Index != stream.Index;
- }
- }
-
- // Look for the first audio track
- foreach (MediaStream currentStream in MediaStreams)
- {
- if (currentStream.Type == MediaStreamType.Audio)
- {
- return currentStream.Index != stream.Index;
- }
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs
deleted file mode 100644
index e04978502..000000000
--- a/MediaBrowser.Model/Dto/MediaSourceType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- public enum MediaSourceType
- {
- Default = 0,
- Grouping = 1,
- Placeholder = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
deleted file mode 100644
index aa8b33c81..000000000
--- a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Providers;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Dto
-{
- public class MetadataEditorInfo
- {
- public ParentalRating[] ParentalRatingOptions { get; set; }
- public CountryInfo[] Countries { get; set; }
- public CultureDto[] Cultures { get; set; }
- public ExternalIdInfo[] ExternalIdInfos { get; set; }
-
- public string ContentType { get; set; }
- public NameValuePair[] ContentTypeOptions { get; set; }
-
- public MetadataEditorInfo()
- {
- ParentalRatingOptions = new ParentalRating[] { };
- Countries = new CountryInfo[] { };
- Cultures = new CultureDto[] { };
- ExternalIdInfos = new ExternalIdInfo[] { };
- ContentTypeOptions = new NameValuePair[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs
deleted file mode 100644
index d3931516c..000000000
--- a/MediaBrowser.Model/Dto/NameIdPair.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- public class NameIdPair
- {
- /// <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; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/NameValuePair.cs b/MediaBrowser.Model/Dto/NameValuePair.cs
deleted file mode 100644
index a6e687949..000000000
--- a/MediaBrowser.Model/Dto/NameValuePair.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- public class NameValuePair
- {
- public NameValuePair()
- {
-
- }
-
- public NameValuePair(string name, string value)
- {
- Name = name;
- Value = value;
- }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the value.
- /// </summary>
- /// <value>The value.</value>
- public string Value { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/RatingType.cs b/MediaBrowser.Model/Dto/RatingType.cs
deleted file mode 100644
index f151adce9..000000000
--- a/MediaBrowser.Model/Dto/RatingType.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- public enum RatingType
- {
- Score,
- Likes
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/RecommendationDto.cs b/MediaBrowser.Model/Dto/RecommendationDto.cs
deleted file mode 100644
index 275f97c28..000000000
--- a/MediaBrowser.Model/Dto/RecommendationDto.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Dto
-{
- public class RecommendationDto
- {
- public BaseItemDto[] Items { get; set; }
-
- public RecommendationType RecommendationType { get; set; }
-
- public string BaselineItemName { get; set; }
-
- public string CategoryId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/RecommendationType.cs b/MediaBrowser.Model/Dto/RecommendationType.cs
deleted file mode 100644
index 1adf9b082..000000000
--- a/MediaBrowser.Model/Dto/RecommendationType.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- public enum RecommendationType
- {
- SimilarToRecentlyPlayed = 0,
-
- SimilarToLikedItem = 1,
-
- HasDirectorFromRecentlyPlayed = 2,
-
- HasActorFromRecentlyPlayed = 3,
-
- HasLikedDirector = 4,
-
- HasLikedActor = 5
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/SubtitleDownloadOptions.cs b/MediaBrowser.Model/Dto/SubtitleDownloadOptions.cs
deleted file mode 100644
index 02a016406..000000000
--- a/MediaBrowser.Model/Dto/SubtitleDownloadOptions.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- public class SubtitleDownloadOptions
- {
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the media source identifier.
- /// </summary>
- /// <value>The media source identifier.</value>
- public string MediaSourceId { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the stream.
- /// </summary>
- /// <value>The index of the stream.</value>
- public int StreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- public string Format { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
deleted file mode 100644
index 2b5672896..000000000
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Users;
-using System;
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class UserDto
- /// </summary>
- [DebuggerDisplay("Name = {Name}, ID = {Id}, HasPassword = {HasPassword}")]
- public class UserDto : IItemDto, IHasServerId
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the server.
- /// This is not used by the server and is for client-side usage only.
- /// </summary>
- /// <value>The name of the server.</value>
- public string ServerName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the connect user.
- /// </summary>
- /// <value>The name of the connect user.</value>
- public string ConnectUserName { get; set; }
- /// <summary>
- /// Gets or sets the connect user identifier.
- /// </summary>
- /// <value>The connect user identifier.</value>
- public string ConnectUserId { get; set; }
- /// <summary>
- /// Gets or sets the type of the connect link.
- /// </summary>
- /// <value>The type of the connect link.</value>
- public UserLinkType? ConnectLinkType { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</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; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has password.
- /// </summary>
- /// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value>
- public bool HasPassword { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has configured password.
- /// </summary>
- /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value>
- public bool HasConfiguredPassword { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has configured easy password.
- /// </summary>
- /// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
- public bool HasConfiguredEasyPassword { get; set; }
-
- public bool? EnableAutoLogin { get; set; }
-
- /// <summary>
- /// Gets or sets the last login date.
- /// </summary>
- /// <value>The last login date.</value>
- public DateTime? LastLoginDate { get; set; }
-
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime? LastActivityDate { get; set; }
-
- /// <summary>
- /// Gets or sets the configuration.
- /// </summary>
- /// <value>The configuration.</value>
- public UserConfiguration Configuration { get; set; }
-
- /// <summary>
- /// Gets or sets the policy.
- /// </summary>
- /// <value>The policy.</value>
- public UserPolicy Policy { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image aspect ratio.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- public double? PrimaryImageAspectRatio { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="UserDto"/> class.
- /// </summary>
- public UserDto()
- {
- Configuration = new UserConfiguration();
- Policy = new UserPolicy();
- }
-
- public override string ToString()
- {
- return Name ?? base.ToString();
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/UserItemDataDto.cs b/MediaBrowser.Model/Dto/UserItemDataDto.cs
deleted file mode 100644
index 507dbb06d..000000000
--- a/MediaBrowser.Model/Dto/UserItemDataDto.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class UserItemDataDto
- /// </summary>
- public class UserItemDataDto
- {
- /// <summary>
- /// Gets or sets the rating.
- /// </summary>
- /// <value>The rating.</value>
- public double? Rating { get; set; }
-
- /// <summary>
- /// Gets or sets the played percentage.
- /// </summary>
- /// <value>The played percentage.</value>
- public double? PlayedPercentage { get; set; }
-
- /// <summary>
- /// Gets or sets the unplayed item count.
- /// </summary>
- /// <value>The unplayed item count.</value>
- public int? UnplayedItemCount { get; set; }
-
- /// <summary>
- /// Gets or sets the playback position ticks.
- /// </summary>
- /// <value>The playback position ticks.</value>
- public long PlaybackPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the play count.
- /// </summary>
- /// <value>The play count.</value>
- public int PlayCount { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>true</c> if this instance is favorite; otherwise, <c>false</c>.</value>
- public bool IsFavorite { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is likes.
- /// </summary>
- /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
- public bool? Likes { get; set; }
-
- /// <summary>
- /// Gets or sets the last played date.
- /// </summary>
- /// <value>The last played date.</value>
- public DateTime? LastPlayedDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is played.
- /// </summary>
- /// <value><c>true</c> if played; otherwise, <c>false</c>.</value>
- public bool Played { get; set; }
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/ChapterInfo.cs b/MediaBrowser.Model/Entities/ChapterInfo.cs
deleted file mode 100644
index 7e5700965..000000000
--- a/MediaBrowser.Model/Entities/ChapterInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class ChapterInfo
- /// </summary>
- public class ChapterInfo
- {
- /// <summary>
- /// Gets or sets the start position ticks.
- /// </summary>
- /// <value>The start position ticks.</value>
- public long StartPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the image path.
- /// </summary>
- /// <value>The image path.</value>
- public string ImagePath { get; set; }
- public DateTime ImageDateModified { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs
deleted file mode 100644
index b1cbb9ac3..000000000
--- a/MediaBrowser.Model/Entities/CollectionType.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- public static class CollectionType
- {
- public const string Movies = "movies";
-
- public const string TvShows = "tvshows";
-
- public const string Music = "music";
-
- public const string MusicVideos = "musicvideos";
-
- public const string Trailers = "trailers";
-
- public const string HomeVideos = "homevideos";
-
- public const string BoxSets = "boxsets";
-
- public const string Books = "books";
- public const string Photos = "photos";
- public const string Games = "games";
- public const string Channels = "channels";
- public const string LiveTv = "livetv";
- public const string Playlists = "playlists";
- public const string Folders = "folders";
- }
-
- public static class SpecialFolder
- {
- public const string LiveTvNowPlaying = "LiveTvNowPlaying";
- public const string LiveTvChannels = "LiveTvChannels";
- public const string LiveTvRecordingGroups = "LiveTvRecordingGroups";
-
- public const string TvShowSeries = "TvShowSeries";
- public const string TvGenres = "TvGenres";
- public const string TvGenre = "TvGenre";
- public const string TvLatest = "TvLatest";
- public const string TvNextUp = "TvNextUp";
- public const string TvResume = "TvResume";
- public const string TvFavoriteSeries = "TvFavoriteSeries";
- public const string TvFavoriteEpisodes = "TvFavoriteEpisodes";
-
- public const string MovieLatest = "MovieLatest";
- public const string MovieResume = "MovieResume";
- public const string MovieMovies = "MovieMovies";
- public const string MovieCollections = "MovieCollections";
- public const string MovieFavorites = "MovieFavorites";
- public const string MovieGenres = "MovieGenres";
- public const string MovieGenre = "MovieGenre";
-
- public const string MusicArtists = "MusicArtists";
- public const string MusicAlbumArtists = "MusicAlbumArtists";
- public const string MusicAlbums = "MusicAlbums";
- public const string MusicGenres = "MusicGenres";
- public const string MusicLatest = "MusicLatest";
- public const string MusicPlaylists = "MusicPlaylists";
- public const string MusicSongs = "MusicSongs";
- public const string MusicFavorites = "MusicFavorites";
- public const string MusicFavoriteArtists = "MusicFavoriteArtists";
- public const string MusicFavoriteAlbums = "MusicFavoriteAlbums";
- public const string MusicFavoriteSongs = "MusicFavoriteSongs";
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/DisplayPreferences.cs b/MediaBrowser.Model/Entities/DisplayPreferences.cs
deleted file mode 100644
index 0ba8eaa4f..000000000
--- a/MediaBrowser.Model/Entities/DisplayPreferences.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using MediaBrowser.Model.Drawing;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Defines the display preferences for any item that supports them (usually Folders)
- /// </summary>
- public class DisplayPreferences
- {
- /// <summary>
- /// The image scale
- /// </summary>
- private const double ImageScale = .9;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DisplayPreferences" /> class.
- /// </summary>
- public DisplayPreferences()
- {
- RememberIndexing = false;
- PrimaryImageHeight = 250;
- PrimaryImageWidth = 250;
- ShowBackdrop = true;
- CustomPrefs = new Dictionary<string, string>();
- }
-
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets the type of the view.
- /// </summary>
- /// <value>The type of the view.</value>
- public string ViewType { get; set; }
- /// <summary>
- /// Gets or sets the sort by.
- /// </summary>
- /// <value>The sort by.</value>
- public string SortBy { get; set; }
- /// <summary>
- /// Gets or sets the index by.
- /// </summary>
- /// <value>The index by.</value>
- public string IndexBy { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [remember indexing].
- /// </summary>
- /// <value><c>true</c> if [remember indexing]; otherwise, <c>false</c>.</value>
- public bool RememberIndexing { get; set; }
- /// <summary>
- /// Gets or sets the height of the primary image.
- /// </summary>
- /// <value>The height of the primary image.</value>
- public int PrimaryImageHeight { get; set; }
- /// <summary>
- /// Gets or sets the width of the primary image.
- /// </summary>
- /// <value>The width of the primary image.</value>
- public int PrimaryImageWidth { get; set; }
- /// <summary>
- /// Gets or sets the custom prefs.
- /// </summary>
- /// <value>The custom prefs.</value>
- public Dictionary<string, string> CustomPrefs { get; set; }
- /// <summary>
- /// Gets or sets the scroll direction.
- /// </summary>
- /// <value>The scroll direction.</value>
- public ScrollDirection ScrollDirection { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether to show backdrops on this item.
- /// </summary>
- /// <value><c>true</c> if showing backdrops; otherwise, <c>false</c>.</value>
- public bool ShowBackdrop { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [remember sorting].
- /// </summary>
- /// <value><c>true</c> if [remember sorting]; otherwise, <c>false</c>.</value>
- public bool RememberSorting { get; set; }
- /// <summary>
- /// Gets or sets the sort order.
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder SortOrder { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [show sidebar].
- /// </summary>
- /// <value><c>true</c> if [show sidebar]; otherwise, <c>false</c>.</value>
- public bool ShowSidebar { get; set; }
- /// <summary>
- /// Gets or sets the client
- /// </summary>
- public string Client { get; set; }
-
- /// <summary>
- /// Increases the size of the image.
- /// </summary>
- public void IncreaseImageSize()
- {
- double newWidth = PrimaryImageWidth / ImageScale;
-
- ImageSize size = DrawingUtils.Resize(PrimaryImageWidth, PrimaryImageHeight, newWidth, null, null, null);
-
- PrimaryImageWidth = Convert.ToInt32(size.Width);
- PrimaryImageHeight = Convert.ToInt32(size.Height);
- }
-
- /// <summary>
- /// Decreases the size of the image.
- /// </summary>
- public void DecreaseImageSize()
- {
- ImageSize size = DrawingUtils.Scale(PrimaryImageWidth, PrimaryImageHeight, ImageScale);
-
- PrimaryImageWidth = Convert.ToInt32(size.Width);
- PrimaryImageHeight = Convert.ToInt32(size.Height);
- }
- }
-}
diff --git a/MediaBrowser.Model/Entities/EmptyRequestResult.cs b/MediaBrowser.Model/Entities/EmptyRequestResult.cs
deleted file mode 100644
index 5c9a725fd..000000000
--- a/MediaBrowser.Model/Entities/EmptyRequestResult.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- public class EmptyRequestResult
- {
- }
-}
diff --git a/MediaBrowser.Model/Entities/ExtraType.cs b/MediaBrowser.Model/Entities/ExtraType.cs
deleted file mode 100644
index ab8da58c0..000000000
--- a/MediaBrowser.Model/Entities/ExtraType.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- public enum ExtraType
- {
- Clip = 1,
- Trailer = 2,
- BehindTheScenes = 3,
- DeletedScene = 4,
- Interview = 5,
- Scene = 6,
- Sample = 7,
- ThemeSong = 8,
- ThemeVideo = 9
- }
-}
diff --git a/MediaBrowser.Model/Entities/IHasProviderIds.cs b/MediaBrowser.Model/Entities/IHasProviderIds.cs
deleted file mode 100644
index 796850dbd..000000000
--- a/MediaBrowser.Model/Entities/IHasProviderIds.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repition by using extension methods
- /// </summary>
- public interface IHasProviderIds
- {
- /// <summary>
- /// Gets or sets the provider ids.
- /// </summary>
- /// <value>The provider ids.</value>
- Dictionary<string, string> ProviderIds { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/ImageType.cs b/MediaBrowser.Model/Entities/ImageType.cs
deleted file mode 100644
index 6e0ba717f..000000000
--- a/MediaBrowser.Model/Entities/ImageType.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum ImageType
- /// </summary>
- public enum ImageType
- {
- /// <summary>
- /// The primary
- /// </summary>
- Primary = 0,
- /// <summary>
- /// The art
- /// </summary>
- Art = 1,
- /// <summary>
- /// The backdrop
- /// </summary>
- Backdrop = 2,
- /// <summary>
- /// The banner
- /// </summary>
- Banner = 3,
- /// <summary>
- /// The logo
- /// </summary>
- Logo = 4,
- /// <summary>
- /// The thumb
- /// </summary>
- Thumb = 5,
- /// <summary>
- /// The disc
- /// </summary>
- Disc = 6,
- /// <summary>
- /// The box
- /// </summary>
- Box = 7,
- /// <summary>
- /// The screenshot
- /// </summary>
- Screenshot = 8,
- /// <summary>
- /// The menu
- /// </summary>
- Menu = 9,
- /// <summary>
- /// The chapter image
- /// </summary>
- Chapter = 10,
- /// <summary>
- /// The box rear
- /// </summary>
- BoxRear = 11
- }
-}
diff --git a/MediaBrowser.Model/Entities/IsoType.cs b/MediaBrowser.Model/Entities/IsoType.cs
deleted file mode 100644
index 567b98ab9..000000000
--- a/MediaBrowser.Model/Entities/IsoType.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum IsoType
- /// </summary>
- public enum IsoType
- {
- /// <summary>
- /// The DVD
- /// </summary>
- Dvd,
- /// <summary>
- /// The blu ray
- /// </summary>
- BluRay
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/ItemReview.cs b/MediaBrowser.Model/Entities/ItemReview.cs
deleted file mode 100644
index e1a32aa11..000000000
--- a/MediaBrowser.Model/Entities/ItemReview.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class ItemReview
- /// </summary>
- public class ItemReview
- {
- /// <summary>
- /// Gets or sets the name of the reviewer.
- /// </summary>
- /// <value>The name of the reviewer.</value>
- public string ReviewerName { get; set; }
-
- /// <summary>
- /// Gets or sets the publisher.
- /// </summary>
- /// <value>The publisher.</value>
- public string Publisher { get; set; }
-
- /// <summary>
- /// Gets or sets the date.
- /// </summary>
- /// <value>The date.</value>
- public DateTime Date { get; set; }
-
- /// <summary>
- /// Gets or sets the score.
- /// </summary>
- /// <value>The score.</value>
- public float? Score { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="ItemReview"/> is likes.
- /// </summary>
- /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
- public bool? Likes { get; set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the caption.
- /// </summary>
- /// <value>The caption.</value>
- public string Caption { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
deleted file mode 100644
index 8995e0888..000000000
--- a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class LibraryUpdateInfo
- /// </summary>
- public class LibraryUpdateInfo
- {
- /// <summary>
- /// Gets or sets the folders added to.
- /// </summary>
- /// <value>The folders added to.</value>
- public string[] FoldersAddedTo { get; set; }
- /// <summary>
- /// Gets or sets the folders removed from.
- /// </summary>
- /// <value>The folders removed from.</value>
- public string[] FoldersRemovedFrom { get; set; }
-
- /// <summary>
- /// Gets or sets the items added.
- /// </summary>
- /// <value>The items added.</value>
- public string[] ItemsAdded { get; set; }
-
- /// <summary>
- /// Gets or sets the items removed.
- /// </summary>
- /// <value>The items removed.</value>
- public string[] ItemsRemoved { get; set; }
-
- /// <summary>
- /// Gets or sets the items updated.
- /// </summary>
- /// <value>The items updated.</value>
- public string[] ItemsUpdated { get; set; }
-
- public string[] CollectionFolders { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LibraryUpdateInfo"/> class.
- /// </summary>
- public LibraryUpdateInfo()
- {
- FoldersAddedTo = new string[] { };
- FoldersRemovedFrom = new string[] { };
- ItemsAdded = new string[] { };
- ItemsRemoved = new string[] { };
- ItemsUpdated = new string[] { };
- CollectionFolders = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Entities/LocationType.cs b/MediaBrowser.Model/Entities/LocationType.cs
deleted file mode 100644
index 84de803aa..000000000
--- a/MediaBrowser.Model/Entities/LocationType.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum LocationType
- /// </summary>
- public enum LocationType
- {
- /// <summary>
- /// The file system
- /// </summary>
- FileSystem = 0,
- /// <summary>
- /// The remote
- /// </summary>
- Remote = 1,
- /// <summary>
- /// The virtual
- /// </summary>
- Virtual = 2,
- /// <summary>
- /// The offline
- /// </summary>
- Offline = 3
- }
-}
diff --git a/MediaBrowser.Model/Entities/MBRegistrationRecord.cs b/MediaBrowser.Model/Entities/MBRegistrationRecord.cs
deleted file mode 100644
index 00176fb34..000000000
--- a/MediaBrowser.Model/Entities/MBRegistrationRecord.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Entities
-{
- public class MBRegistrationRecord
- {
- public DateTime ExpirationDate { get; set; }
- public bool IsRegistered { get; set; }
- public bool RegChecked { get; set; }
- public bool RegError { get; set; }
- public bool TrialVersion { get; set; }
- public bool IsValid { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
deleted file mode 100644
index 8a402e6f4..000000000
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ /dev/null
@@ -1,454 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Extensions;
-using System.Diagnostics;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class MediaStream
- /// </summary>
- [DebuggerDisplay("StreamType = {Type}")]
- public class MediaStream
- {
- /// <summary>
- /// Gets or sets the codec.
- /// </summary>
- /// <value>The codec.</value>
- public string Codec { get; set; }
-
- /// <summary>
- /// Gets or sets the codec tag.
- /// </summary>
- /// <value>The codec tag.</value>
- public string CodecTag { get; set; }
-
- /// <summary>
- /// Gets or sets the language.
- /// </summary>
- /// <value>The language.</value>
- public string Language { get; set; }
-
- /// <summary>
- /// Gets or sets the comment.
- /// </summary>
- /// <value>The comment.</value>
- public string Comment { get; set; }
-
- public string TimeBase { get; set; }
- public string CodecTimeBase { get; set; }
-
- public string Title { get; set; }
-
- public string DisplayTitle
- {
- get
- {
- if (Type == MediaStreamType.Audio)
- {
- //if (!string.IsNullOrEmpty(Title))
- //{
- // return AddLanguageIfNeeded(Title);
- //}
-
- List<string> attributes = new List<string>();
-
- if (!string.IsNullOrEmpty(Language))
- {
- attributes.Add(StringHelper.FirstToUpper(Language));
- }
- if (!string.IsNullOrEmpty(Codec) && !StringHelper.EqualsIgnoreCase(Codec, "dca"))
- {
- attributes.Add(AudioCodec.GetFriendlyName(Codec));
- }
- else if (!string.IsNullOrEmpty(Profile) && !StringHelper.EqualsIgnoreCase(Profile, "lc"))
- {
- attributes.Add(Profile);
- }
-
- if (!string.IsNullOrEmpty(ChannelLayout))
- {
- attributes.Add(ChannelLayout);
- }
- else if (Channels.HasValue)
- {
- attributes.Add(StringHelper.ToStringCultureInvariant(Channels.Value) + " ch");
- }
- if (IsDefault)
- {
- attributes.Add("Default");
- }
-
- return string.Join(" ", attributes.ToArray(attributes.Count));
- }
-
- if (Type == MediaStreamType.Video)
- {
- List<string> attributes = new List<string>();
-
- var resolutionText = GetResolutionText();
-
- if (!string.IsNullOrEmpty(resolutionText))
- {
- attributes.Add(resolutionText);
- }
-
- if (!string.IsNullOrEmpty(Codec))
- {
- attributes.Add(Codec.ToUpper());
- }
-
- return string.Join(" ", attributes.ToArray(attributes.Count));
- }
-
- if (Type == MediaStreamType.Subtitle)
- {
- //if (!string.IsNullOrEmpty(Title))
- //{
- // return AddLanguageIfNeeded(Title);
- //}
-
- List<string> attributes = new List<string>();
-
- if (!string.IsNullOrEmpty(Language))
- {
- attributes.Add(StringHelper.FirstToUpper(Language));
- }
- else
- {
- attributes.Add("Und");
- }
-
- if (IsDefault)
- {
- attributes.Add("Default");
- }
-
- if (IsForced)
- {
- attributes.Add("Forced");
- }
-
- string name = string.Join(" ", attributes.ToArray(attributes.Count));
-
- return name;
- }
-
- if (Type == MediaStreamType.Video)
- {
-
- }
-
- return null;
- }
- }
-
- private string GetResolutionText()
- {
- var i = this;
-
- if (i.Width.HasValue)
- {
- if (i.Width >= 3800)
- {
- return "4K";
- }
- if (i.Width >= 2500)
- {
- if (i.IsInterlaced)
- {
- return "1440I";
- }
- return "1440P";
- }
- if (i.Width >= 1900)
- {
- if (i.IsInterlaced)
- {
- return "1080I";
- }
- return "1080P";
- }
- if (i.Width >= 1260)
- {
- if (i.IsInterlaced)
- {
- return "720I";
- }
- return "720P";
- }
- if (i.Width >= 700)
- {
-
- if (i.IsInterlaced)
- {
- return "480I";
- }
- return "480P";
- }
-
- }
- return null;
- }
-
- private string AddLanguageIfNeeded(string title)
- {
- if (!string.IsNullOrEmpty(Language) &&
- !string.Equals(Language, "und", StringComparison.OrdinalIgnoreCase) &&
- !IsLanguageInTitle(title, Language))
- {
- title = StringHelper.FirstToUpper(Language) + " " + title;
- }
-
- return title;
- }
-
- private bool IsLanguageInTitle(string title, string language)
- {
- if (title.IndexOf(Language, StringComparison.OrdinalIgnoreCase) != -1)
- {
- return true;
- }
-
- return false;
- }
-
- public string NalLengthSize { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is interlaced.
- /// </summary>
- /// <value><c>true</c> if this instance is interlaced; otherwise, <c>false</c>.</value>
- public bool IsInterlaced { get; set; }
-
- public bool? IsAVC { get; set; }
-
- /// <summary>
- /// Gets or sets the channel layout.
- /// </summary>
- /// <value>The channel layout.</value>
- public string ChannelLayout { get; set; }
-
- /// <summary>
- /// Gets or sets the bit rate.
- /// </summary>
- /// <value>The bit rate.</value>
- public int? BitRate { get; set; }
-
- /// <summary>
- /// Gets or sets the bit depth.
- /// </summary>
- /// <value>The bit depth.</value>
- public int? BitDepth { get; set; }
-
- /// <summary>
- /// Gets or sets the reference frames.
- /// </summary>
- /// <value>The reference frames.</value>
- public int? RefFrames { get; set; }
-
- /// <summary>
- /// Gets or sets the length of the packet.
- /// </summary>
- /// <value>The length of the packet.</value>
- public int? PacketLength { get; set; }
-
- /// <summary>
- /// Gets or sets the channels.
- /// </summary>
- /// <value>The channels.</value>
- public int? Channels { get; set; }
-
- /// <summary>
- /// Gets or sets the sample rate.
- /// </summary>
- /// <value>The sample rate.</value>
- public int? SampleRate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is default.
- /// </summary>
- /// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
- public bool IsDefault { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is forced.
- /// </summary>
- /// <value><c>true</c> if this instance is forced; otherwise, <c>false</c>.</value>
- public bool IsForced { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the average frame rate.
- /// </summary>
- /// <value>The average frame rate.</value>
- public float? AverageFrameRate { get; set; }
-
- /// <summary>
- /// Gets or sets the real frame rate.
- /// </summary>
- /// <value>The real frame rate.</value>
- public float? RealFrameRate { get; set; }
-
- /// <summary>
- /// Gets or sets the profile.
- /// </summary>
- /// <value>The profile.</value>
- public string Profile { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public MediaStreamType Type { get; set; }
-
- /// <summary>
- /// Gets or sets the aspect ratio.
- /// </summary>
- /// <value>The aspect ratio.</value>
- public string AspectRatio { get; set; }
-
- /// <summary>
- /// Gets or sets the index.
- /// </summary>
- /// <value>The index.</value>
- public int Index { get; set; }
-
- /// <summary>
- /// Gets or sets the score.
- /// </summary>
- /// <value>The score.</value>
- public int? Score { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is external.
- /// </summary>
- /// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value>
- public bool IsExternal { get; set; }
-
- /// <summary>
- /// Gets or sets the method.
- /// </summary>
- /// <value>The method.</value>
- public SubtitleDeliveryMethod? DeliveryMethod { get; set; }
- /// <summary>
- /// Gets or sets the delivery URL.
- /// </summary>
- /// <value>The delivery URL.</value>
- public string DeliveryUrl { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is external URL.
- /// </summary>
- /// <value><c>null</c> if [is external URL] contains no value, <c>true</c> if [is external URL]; otherwise, <c>false</c>.</value>
- public bool? IsExternalUrl { get; set; }
-
- public bool IsTextSubtitleStream
- {
- get
- {
- if (Type != MediaStreamType.Subtitle) return false;
-
- if (string.IsNullOrEmpty(Codec) && !IsExternal)
- {
- return false;
- }
-
- return IsTextFormat(Codec);
- }
- }
-
- public static bool IsTextFormat(string format)
- {
- string codec = format ?? string.Empty;
-
- // sub = external .sub file
-
- return StringHelper.IndexOfIgnoreCase(codec, "pgs") == -1 &&
- StringHelper.IndexOfIgnoreCase(codec, "dvd") == -1 &&
- StringHelper.IndexOfIgnoreCase(codec, "dvbsub") == -1 &&
- !StringHelper.EqualsIgnoreCase(codec, "sub") &&
- !StringHelper.EqualsIgnoreCase(codec, "dvb_subtitle");
- }
-
- public bool SupportsSubtitleConversionTo(string toCodec)
- {
- if (!IsTextSubtitleStream)
- {
- return false;
- }
-
- var fromCodec = Codec;
-
- // Can't convert from this
- if (StringHelper.EqualsIgnoreCase(fromCodec, "ass"))
- {
- return false;
- }
- if (StringHelper.EqualsIgnoreCase(fromCodec, "ssa"))
- {
- return false;
- }
-
- // Can't convert to this
- if (StringHelper.EqualsIgnoreCase(toCodec, "ass"))
- {
- return false;
- }
- if (StringHelper.EqualsIgnoreCase(toCodec, "ssa"))
- {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports external stream].
- /// </summary>
- /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
- public bool SupportsExternalStream { get; set; }
-
- /// <summary>
- /// Gets or sets the filename.
- /// </summary>
- /// <value>The filename.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the external identifier.
- /// </summary>
- /// <value>The external identifier.</value>
- public string ExternalId { get; set; }
-
- /// <summary>
- /// Gets or sets the pixel format.
- /// </summary>
- /// <value>The pixel format.</value>
- public string PixelFormat { get; set; }
-
- /// <summary>
- /// Gets or sets the level.
- /// </summary>
- /// <value>The level.</value>
- public double? Level { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is anamorphic.
- /// </summary>
- /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
- public bool? IsAnamorphic { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/MediaStreamType.cs b/MediaBrowser.Model/Entities/MediaStreamType.cs
deleted file mode 100644
index 084a411f9..000000000
--- a/MediaBrowser.Model/Entities/MediaStreamType.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum MediaStreamType
- /// </summary>
- public enum MediaStreamType
- {
- /// <summary>
- /// The audio
- /// </summary>
- Audio,
- /// <summary>
- /// The video
- /// </summary>
- Video,
- /// <summary>
- /// The subtitle
- /// </summary>
- Subtitle,
- /// <summary>
- /// The embedded image
- /// </summary>
- EmbeddedImage
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/MediaType.cs b/MediaBrowser.Model/Entities/MediaType.cs
deleted file mode 100644
index 0c9bde6fb..000000000
--- a/MediaBrowser.Model/Entities/MediaType.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class MediaType
- /// </summary>
- public class MediaType
- {
- /// <summary>
- /// The video
- /// </summary>
- public const string Video = "Video";
- /// <summary>
- /// The audio
- /// </summary>
- public const string Audio = "Audio";
- /// <summary>
- /// The game
- /// </summary>
- public const string Game = "Game";
- /// <summary>
- /// The photo
- /// </summary>
- public const string Photo = "Photo";
- /// <summary>
- /// The book
- /// </summary>
- public const string Book = "Book";
- }
-}
diff --git a/MediaBrowser.Model/Entities/MediaUrl.cs b/MediaBrowser.Model/Entities/MediaUrl.cs
deleted file mode 100644
index 2e17bba8a..000000000
--- a/MediaBrowser.Model/Entities/MediaUrl.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- public class MediaUrl
- {
- public string Url { get; set; }
- public string Name { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/MetadataFields.cs b/MediaBrowser.Model/Entities/MetadataFields.cs
deleted file mode 100644
index a99fd0fe0..000000000
--- a/MediaBrowser.Model/Entities/MetadataFields.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum MetadataFields
- /// </summary>
- public enum MetadataFields
- {
- /// <summary>
- /// The cast
- /// </summary>
- Cast,
- /// <summary>
- /// The genres
- /// </summary>
- Genres,
- /// <summary>
- /// The production locations
- /// </summary>
- ProductionLocations,
- /// <summary>
- /// The studios
- /// </summary>
- Studios,
- /// <summary>
- /// The tags
- /// </summary>
- Tags,
- /// <summary>
- /// The name
- /// </summary>
- Name,
- /// <summary>
- /// The overview
- /// </summary>
- Overview,
- /// <summary>
- /// The runtime
- /// </summary>
- Runtime,
- /// <summary>
- /// The official rating
- /// </summary>
- OfficialRating,
- /// <summary>
- /// The images
- /// </summary>
- Images,
- /// <summary>
- /// The backdrops
- /// </summary>
- Backdrops,
- /// <summary>
- /// The screenshots
- /// </summary>
- Screenshots
- }
-}
diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProviders.cs
deleted file mode 100644
index efd4339d5..000000000
--- a/MediaBrowser.Model/Entities/MetadataProviders.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum MetadataProviders
- /// </summary>
- public enum MetadataProviders
- {
- Gamesdb = 1,
- /// <summary>
- /// The imdb
- /// </summary>
- Imdb = 2,
- /// <summary>
- /// The TMDB
- /// </summary>
- Tmdb = 3,
- /// <summary>
- /// The TVDB
- /// </summary>
- Tvdb = 4,
- /// <summary>
- /// The tvcom
- /// </summary>
- Tvcom = 5,
- /// <summary>
- /// Tmdb Collection Id
- /// </summary>
- TmdbCollection = 7,
- MusicBrainzAlbum = 8,
- MusicBrainzAlbumArtist = 9,
- MusicBrainzArtist = 10,
- MusicBrainzReleaseGroup = 11,
- Zap2It = 12,
- TvRage = 15,
- AudioDbArtist = 16,
- AudioDbAlbum = 17,
- MusicBrainzTrack = 18,
- TvMaze = 19
- }
-}
diff --git a/MediaBrowser.Model/Entities/PackageReviewInfo.cs b/MediaBrowser.Model/Entities/PackageReviewInfo.cs
deleted file mode 100644
index 52500a41e..000000000
--- a/MediaBrowser.Model/Entities/PackageReviewInfo.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Entities
-{
- public class PackageReviewInfo
- {
- /// <summary>
- /// The package id (database key) for this review
- /// </summary>
- public int id { get; set; }
-
- /// <summary>
- /// The rating value
- /// </summary>
- public int rating { get; set; }
-
- /// <summary>
- /// Whether or not this review recommends this item
- /// </summary>
- public bool recommend { get; set; }
-
- /// <summary>
- /// A short description of the review
- /// </summary>
- public string title { get; set; }
-
- /// <summary>
- /// A full review
- /// </summary>
- public string review { get; set; }
-
- /// <summary>
- /// Time of review
- /// </summary>
- public DateTime timestamp { get; set; }
-
- }
-}
diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs
deleted file mode 100644
index e2d1c6574..000000000
--- a/MediaBrowser.Model/Entities/ParentalRating.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class ParentalRating
- /// </summary>
- public class ParentalRating
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the value.
- /// </summary>
- /// <value>The value.</value>
- public int Value { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs
deleted file mode 100644
index bc274972d..000000000
--- a/MediaBrowser.Model/Entities/PersonType.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Struct PersonType
- /// </summary>
- public class PersonType
- {
- /// <summary>
- /// The actor
- /// </summary>
- public const string Actor = "Actor";
- /// <summary>
- /// The director
- /// </summary>
- public const string Director = "Director";
- /// <summary>
- /// The composer
- /// </summary>
- public const string Composer = "Composer";
- /// <summary>
- /// The writer
- /// </summary>
- public const string Writer = "Writer";
- /// <summary>
- /// The guest star
- /// </summary>
- public const string GuestStar = "GuestStar";
- /// <summary>
- /// The producer
- /// </summary>
- public const string Producer = "Producer";
- /// <summary>
- /// The conductor
- /// </summary>
- public const string Conductor = "Conductor";
- /// <summary>
- /// The lyricist
- /// </summary>
- public const string Lyricist = "Lyricist";
- }
-}
diff --git a/MediaBrowser.Model/Entities/PluginSecurityInfo.cs b/MediaBrowser.Model/Entities/PluginSecurityInfo.cs
deleted file mode 100644
index 5cab55013..000000000
--- a/MediaBrowser.Model/Entities/PluginSecurityInfo.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class PluginSecurityInfo
- /// </summary>
- public class PluginSecurityInfo
- {
- /// <summary>
- /// Gets or sets the supporter key.
- /// </summary>
- /// <value>The supporter key.</value>
- public string SupporterKey { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is MB supporter.
- /// </summary>
- /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value>
- public bool IsMBSupporter { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
deleted file mode 100644
index e10232baa..000000000
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Class ProviderIdsExtensions
- /// </summary>
- public static class ProviderIdsExtensions
- {
- /// <summary>
- /// Determines whether [has provider identifier] [the specified instance].
- /// </summary>
- /// <param name="instance">The instance.</param>
- /// <param name="provider">The provider.</param>
- /// <returns><c>true</c> if [has provider identifier] [the specified instance]; otherwise, <c>false</c>.</returns>
- public static bool HasProviderId(this IHasProviderIds instance, MetadataProviders provider)
- {
- return !string.IsNullOrEmpty(instance.GetProviderId(provider.ToString()));
- }
-
- /// <summary>
- /// Gets a provider id
- /// </summary>
- /// <param name="instance">The instance.</param>
- /// <param name="provider">The provider.</param>
- /// <returns>System.String.</returns>
- public static string GetProviderId(this IHasProviderIds instance, MetadataProviders provider)
- {
- return instance.GetProviderId(provider.ToString());
- }
-
- /// <summary>
- /// Gets a provider id
- /// </summary>
- /// <param name="instance">The instance.</param>
- /// <param name="name">The name.</param>
- /// <returns>System.String.</returns>
- public static string GetProviderId(this IHasProviderIds instance, string name)
- {
- if (instance == null)
- {
- throw new ArgumentNullException("instance");
- }
-
- if (instance.ProviderIds == null)
- {
- return null;
- }
-
- string id;
- instance.ProviderIds.TryGetValue(name, out id);
- return id;
- }
-
- /// <summary>
- /// Sets a provider id
- /// </summary>
- /// <param name="instance">The instance.</param>
- /// <param name="name">The name.</param>
- /// <param name="value">The value.</param>
- public static void SetProviderId(this IHasProviderIds instance, string name, string value)
- {
- if (instance == null)
- {
- throw new ArgumentNullException("instance");
- }
-
- // If it's null remove the key from the dictionary
- if (string.IsNullOrEmpty(value))
- {
- if (instance.ProviderIds != null)
- {
- if (instance.ProviderIds.ContainsKey(name))
- {
- instance.ProviderIds.Remove(name);
- }
- }
- }
- else
- {
- // Ensure it exists
- if (instance.ProviderIds == null)
- {
- instance.ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
- instance.ProviderIds[name] = value;
- }
- }
-
- /// <summary>
- /// Sets a provider id
- /// </summary>
- /// <param name="instance">The instance.</param>
- /// <param name="provider">The provider.</param>
- /// <param name="value">The value.</param>
- public static void SetProviderId(this IHasProviderIds instance, MetadataProviders provider, string value)
- {
- instance.SetProviderId(provider.ToString(), value);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/ScrollDirection.cs b/MediaBrowser.Model/Entities/ScrollDirection.cs
deleted file mode 100644
index ed2210300..000000000
--- a/MediaBrowser.Model/Entities/ScrollDirection.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum ScrollDirection
- /// </summary>
- public enum ScrollDirection
- {
- /// <summary>
- /// The horizontal
- /// </summary>
- Horizontal,
- /// <summary>
- /// The vertical
- /// </summary>
- Vertical
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/SeriesStatus.cs b/MediaBrowser.Model/Entities/SeriesStatus.cs
deleted file mode 100644
index d04a2856c..000000000
--- a/MediaBrowser.Model/Entities/SeriesStatus.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum SeriesStatus
- /// </summary>
- public enum SeriesStatus
- {
- /// <summary>
- /// The continuing
- /// </summary>
- Continuing,
- /// <summary>
- /// The ended
- /// </summary>
- Ended
- }
-}
diff --git a/MediaBrowser.Model/Entities/SortOrder.cs b/MediaBrowser.Model/Entities/SortOrder.cs
deleted file mode 100644
index 5130449ba..000000000
--- a/MediaBrowser.Model/Entities/SortOrder.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum SortOrder
- /// </summary>
- public enum SortOrder
- {
- /// <summary>
- /// The ascending
- /// </summary>
- Ascending,
- /// <summary>
- /// The descending
- /// </summary>
- Descending
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/TrailerType.cs b/MediaBrowser.Model/Entities/TrailerType.cs
deleted file mode 100644
index 085f461cf..000000000
--- a/MediaBrowser.Model/Entities/TrailerType.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- public enum TrailerType
- {
- ComingSoonToTheaters = 1,
- ComingSoonToDvd = 2,
- ComingSoonToStreaming = 3,
- Archive = 4,
- LocalTrailer = 5
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/UserDataSaveReason.cs b/MediaBrowser.Model/Entities/UserDataSaveReason.cs
deleted file mode 100644
index d9691f395..000000000
--- a/MediaBrowser.Model/Entities/UserDataSaveReason.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum UserDataSaveReason
- /// </summary>
- public enum UserDataSaveReason
- {
- /// <summary>
- /// The playback start
- /// </summary>
- PlaybackStart = 1,
- /// <summary>
- /// The playback progress
- /// </summary>
- PlaybackProgress = 2,
- /// <summary>
- /// The playback finished
- /// </summary>
- PlaybackFinished = 3,
- /// <summary>
- /// The toggle played
- /// </summary>
- TogglePlayed = 4,
- /// <summary>
- /// The update user rating
- /// </summary>
- UpdateUserRating = 5,
- /// <summary>
- /// The import
- /// </summary>
- Import = 6
- }
-}
diff --git a/MediaBrowser.Model/Entities/Video3DFormat.cs b/MediaBrowser.Model/Entities/Video3DFormat.cs
deleted file mode 100644
index 722df4281..000000000
--- a/MediaBrowser.Model/Entities/Video3DFormat.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- public enum Video3DFormat
- {
- HalfSideBySide,
- FullSideBySide,
- FullTopAndBottom,
- HalfTopAndBottom,
- MVC
- }
-}
diff --git a/MediaBrowser.Model/Entities/VideoType.cs b/MediaBrowser.Model/Entities/VideoType.cs
deleted file mode 100644
index 05c2fa32c..000000000
--- a/MediaBrowser.Model/Entities/VideoType.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Enum VideoType
- /// </summary>
- public enum VideoType
- {
- /// <summary>
- /// The video file
- /// </summary>
- VideoFile,
- /// <summary>
- /// The iso
- /// </summary>
- Iso,
- /// <summary>
- /// The DVD
- /// </summary>
- Dvd,
- /// <summary>
- /// The blu ray
- /// </summary>
- BluRay
- }
-}
diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
deleted file mode 100644
index 901090717..000000000
--- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Model.Entities
-{
- /// <summary>
- /// Used to hold information about a user's list of configured virtual folders
- /// </summary>
- public class VirtualFolderInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the locations.
- /// </summary>
- /// <value>The locations.</value>
- public string[] Locations { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the collection.
- /// </summary>
- /// <value>The type of the collection.</value>
- public string CollectionType { get; set; }
-
- public LibraryOptions LibraryOptions { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
- /// </summary>
- public VirtualFolderInfo()
- {
- Locations = new string[] { };
- }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image item identifier.
- /// </summary>
- /// <value>The primary image item identifier.</value>
- public string PrimaryImageItemId { get; set; }
-
- public double? RefreshProgress { get; set; }
- public string RefreshStatus { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/MediaBrowser.Model/Events/GenericEventArgs.cs
deleted file mode 100644
index 3c558577a..000000000
--- a/MediaBrowser.Model/Events/GenericEventArgs.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Events
-{
- /// <summary>
- /// Provides a generic EventArgs subclass that can hold any kind of object
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class GenericEventArgs<T> : EventArgs
- {
- /// <summary>
- /// Gets or sets the argument.
- /// </summary>
- /// <value>The argument.</value>
- public T Argument { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
- /// </summary>
- /// <param name="arg">The argument.</param>
- public GenericEventArgs(T arg)
- {
- Argument = arg;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
- /// </summary>
- public GenericEventArgs()
- {
- }
- }
-}
diff --git a/MediaBrowser.Model/Extensions/LinqExtensions.cs b/MediaBrowser.Model/Extensions/LinqExtensions.cs
deleted file mode 100644
index 09ace42e8..000000000
--- a/MediaBrowser.Model/Extensions/LinqExtensions.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Extensions
-{
- // MoreLINQ - Extensions to LINQ to Objects
- // Copyright (c) 2008 Jonathan Skeet. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- public static class LinqExtensions
- {
- /// <summary>
- /// Returns all distinct elements of the given source, where "distinctness"
- /// is determined via a projection and the default equality comparer for the projected type.
- /// </summary>
- /// <remarks>
- /// This operator uses deferred execution and streams the results, although
- /// a set of already-seen keys is retained. If a key is seen multiple times,
- /// only the first element with that key is returned.
- /// </remarks>
- /// <typeparam name="TSource">Type of the source sequence</typeparam>
- /// <typeparam name="TKey">Type of the projected element</typeparam>
- /// <param name="source">Source sequence</param>
- /// <param name="keySelector">Projection for determining "distinctness"</param>
- /// <returns>A sequence consisting of distinct elements from the source sequence,
- /// comparing them by the specified key projection.</returns>
-
- public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector)
- {
- 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.
- /// </summary>
- /// <remarks>
- /// This operator uses deferred execution and streams the results, although
- /// a set of already-seen keys is retained. If a key is seen multiple times,
- /// only the first element with that key is returned.
- /// </remarks>
- /// <typeparam name="TSource">Type of the source sequence</typeparam>
- /// <typeparam name="TKey">Type of the projected element</typeparam>
- /// <param name="source">Source sequence</param>
- /// <param name="keySelector">Projection for determining "distinctness"</param>
- /// <param name="comparer">The equality comparer to use to determine whether or not keys are equal.
- /// If null, the default equality comparer for <c>TSource</c> is used.</param>
- /// <returns>A sequence consisting of distinct elements from the source sequence,
- /// comparing them by the specified key projection.</returns>
-
- public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
- {
- if (source == null) throw new ArgumentNullException("source");
- if (keySelector == null) throw new ArgumentNullException("keySelector");
- return DistinctByImpl(source, keySelector, comparer);
- }
-
- private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
- Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
- {
- var knownKeys = new HashSet<TKey>(comparer);
- foreach (var element in source)
- {
- if (knownKeys.Add(keySelector(element)))
- {
- yield return element;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs
deleted file mode 100644
index 6fe1793db..000000000
--- a/MediaBrowser.Model/Extensions/ListHelper.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Extensions
-{
- public static class ListHelper
- {
- public static bool ContainsIgnoreCase(string[] list, string value)
- {
- if (value == null)
- {
- throw new ArgumentNullException("value");
- }
-
- foreach (var item in list)
- {
- if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- return false;
- }
-
- public static bool ContainsAnyIgnoreCase(string[] list, string[] values)
- {
- if (values == null)
- {
- throw new ArgumentNullException("values");
- }
-
- foreach (string val in values)
- {
- if (ContainsIgnoreCase(list, val))
- {
- return true;
- }
- }
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs
deleted file mode 100644
index 9cde3bfa4..000000000
--- a/MediaBrowser.Model/Extensions/StringHelper.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.Model.Extensions
-{
- /// <summary>
- /// Isolating these helpers allow this entire project to be easily converted to Java
- /// </summary>
- public static class StringHelper
- {
- /// <summary>
- /// Equalses the ignore case.
- /// </summary>
- /// <param name="str1">The STR1.</param>
- /// <param name="str2">The STR2.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool EqualsIgnoreCase(string str1, string str2)
- {
- return string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Indexes the of ignore case.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="value">The value.</param>
- /// <returns>System.Int32.</returns>
- public static int IndexOfIgnoreCase(string str, string value)
- {
- return str.IndexOf(value, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// To the string culture invariant.
- /// </summary>
- /// <param name="val">The value.</param>
- /// <returns>System.String.</returns>
- public static string ToStringCultureInvariant(int val)
- {
- return val.ToString(CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// To the string culture invariant.
- /// </summary>
- /// <param name="val">The value.</param>
- /// <returns>System.String.</returns>
- public static string ToStringCultureInvariant(long val)
- {
- return val.ToString(CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// To the string culture invariant.
- /// </summary>
- /// <param name="val">The value.</param>
- /// <returns>System.String.</returns>
- public static string ToStringCultureInvariant(double val)
- {
- return val.ToString(CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Trims the start.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="c">The c.</param>
- /// <returns>System.String.</returns>
- public static string TrimStart(string str, char c)
- {
- return str.TrimStart(c);
- }
-
- /// <summary>
- /// Splits the specified string.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="term">The term.</param>
- /// <returns>System.String[].</returns>
- public static string[] RegexSplit(string str, string term)
- {
- return Regex.Split(str, term, RegexOptions.IgnoreCase);
- }
-
- /// <summary>
- /// Splits the specified string.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="term">The term.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>System.String[].</returns>
- public static string[] RegexSplit(string str, string term, int limit)
- {
- return new Regex(term).Split(str, limit);
- }
-
- /// <summary>
- /// Replaces the specified STR.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="oldValue">The old value.</param>
- /// <param name="newValue">The new value.</param>
- /// <param name="comparison">The comparison.</param>
- /// <returns>System.String.</returns>
- public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
- {
- var sb = new StringBuilder();
-
- var previousIndex = 0;
- var index = str.IndexOf(oldValue, comparison);
-
- while (index != -1)
- {
- sb.Append(str.Substring(previousIndex, index - previousIndex));
- sb.Append(newValue);
- index += oldValue.Length;
-
- previousIndex = index;
- index = str.IndexOf(oldValue, index, comparison);
- }
-
- sb.Append(str.Substring(previousIndex));
-
- return sb.ToString();
- }
-
- public static string FirstToUpper(this string str)
- {
- return string.IsNullOrEmpty(str) ? "" : str.Substring(0, 1).ToUpper() + str.Substring(1);
- }
- }
-}
diff --git a/MediaBrowser.Model/Globalization/CountryInfo.cs b/MediaBrowser.Model/Globalization/CountryInfo.cs
deleted file mode 100644
index 16aea8436..000000000
--- a/MediaBrowser.Model/Globalization/CountryInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-
-namespace MediaBrowser.Model.Globalization
-{
- /// <summary>
- /// Class CountryInfo
- /// </summary>
- public class CountryInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the display name.
- /// </summary>
- /// <value>The display name.</value>
- public string DisplayName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the two letter ISO region.
- /// </summary>
- /// <value>The name of the two letter ISO region.</value>
- public string TwoLetterISORegionName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the three letter ISO region.
- /// </summary>
- /// <value>The name of the three letter ISO region.</value>
- public string ThreeLetterISORegionName { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs
deleted file mode 100644
index 414d7b9d4..000000000
--- a/MediaBrowser.Model/Globalization/CultureDto.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-
-namespace MediaBrowser.Model.Globalization
-{
- /// <summary>
- /// Class CultureDto
- /// </summary>
- public class CultureDto
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the display name.
- /// </summary>
- /// <value>The display name.</value>
- public string DisplayName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the two letter ISO language.
- /// </summary>
- /// <value>The name of the two letter ISO language.</value>
- public string TwoLetterISOLanguageName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the three letter ISO language.
- /// </summary>
- /// <value>The name of the three letter ISO language.</value>
- public string ThreeLetterISOLanguageName { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
deleted file mode 100644
index 61f0ebfd3..000000000
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-using System.Globalization;
-
-namespace MediaBrowser.Model.Globalization
-{
- /// <summary>
- /// Interface ILocalizationManager
- /// </summary>
- public interface ILocalizationManager
- {
- /// <summary>
- /// Gets the cultures.
- /// </summary>
- /// <returns>IEnumerable{CultureDto}.</returns>
- CultureDto[] GetCultures();
- /// <summary>
- /// Gets the countries.
- /// </summary>
- /// <returns>IEnumerable{CountryInfo}.</returns>
- CountryInfo[] GetCountries();
- /// <summary>
- /// Gets the parental ratings.
- /// </summary>
- /// <returns>IEnumerable{ParentalRating}.</returns>
- ParentalRating[] GetParentalRatings();
- /// <summary>
- /// Gets the rating level.
- /// </summary>
- /// <param name="rating">The rating.</param>
- /// <returns>System.Int32.</returns>
- int? GetRatingLevel(string rating);
-
- /// <summary>
- /// Gets the localized string.
- /// </summary>
- /// <param name="phrase">The phrase.</param>
- /// <param name="culture">The culture.</param>
- /// <returns>System.String.</returns>
- string GetLocalizedString(string phrase, string culture);
-
- /// <summary>
- /// Gets the localized string.
- /// </summary>
- /// <param name="phrase">The phrase.</param>
- /// <returns>System.String.</returns>
- string GetLocalizedString(string phrase);
-
- /// <summary>
- /// Gets the localization options.
- /// </summary>
- /// <returns>IEnumerable{LocalizatonOption}.</returns>
- LocalizatonOption[] GetLocalizationOptions();
-
- string RemoveDiacritics(string text);
-
- string NormalizeFormKD(string text);
-
- bool HasUnicodeCategory(string value, UnicodeCategory category);
- }
-}
diff --git a/MediaBrowser.Model/Globalization/LocalizatonOption.cs b/MediaBrowser.Model/Globalization/LocalizatonOption.cs
deleted file mode 100644
index 61749cbc3..000000000
--- a/MediaBrowser.Model/Globalization/LocalizatonOption.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Globalization
-{
- public class LocalizatonOption
- {
- public string Name { get; set; }
- public string Value { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/IO/FileSystemEntryInfo.cs b/MediaBrowser.Model/IO/FileSystemEntryInfo.cs
deleted file mode 100644
index f17e2e5c3..000000000
--- a/MediaBrowser.Model/IO/FileSystemEntryInfo.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Class FileSystemEntryInfo
- /// </summary>
- public class FileSystemEntryInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public FileSystemEntryType Type { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/IO/FileSystemEntryType.cs b/MediaBrowser.Model/IO/FileSystemEntryType.cs
deleted file mode 100644
index e7c67c606..000000000
--- a/MediaBrowser.Model/IO/FileSystemEntryType.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Enum FileSystemEntryType
- /// </summary>
- public enum FileSystemEntryType
- {
- /// <summary>
- /// The file
- /// </summary>
- File,
- /// <summary>
- /// The directory
- /// </summary>
- Directory,
- /// <summary>
- /// The network computer
- /// </summary>
- NetworkComputer,
- /// <summary>
- /// The network share
- /// </summary>
- NetworkShare
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs
deleted file mode 100644
index 2aae4bb54..000000000
--- a/MediaBrowser.Model/IO/FileSystemMetadata.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.IO
-{
- public class FileSystemMetadata
- {
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="FileSystemMetadata"/> is exists.
- /// </summary>
- /// <value><c>true</c> if exists; otherwise, <c>false</c>.</value>
- public bool Exists { get; set; }
- /// <summary>
- /// Gets or sets the full name.
- /// </summary>
- /// <value>The full name.</value>
- public string FullName { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the extension.
- /// </summary>
- /// <value>The extension.</value>
- public string Extension { get; set; }
- /// <summary>
- /// Gets or sets the length.
- /// </summary>
- /// <value>The length.</value>
- public long Length { get; set; }
- /// <summary>
- /// Gets or sets the name of the directory.
- /// </summary>
- /// <value>The name of the directory.</value>
- public string DirectoryName { get; set; }
-
- /// <summary>
- /// Gets or sets the last write time UTC.
- /// </summary>
- /// <value>The last write time UTC.</value>
- public DateTime LastWriteTimeUtc { get; set; }
- /// <summary>
- /// Gets or sets the creation time UTC.
- /// </summary>
- /// <value>The creation time UTC.</value>
- public DateTime CreationTimeUtc { get; set; }
- /// <summary>
- /// Gets a value indicating whether this instance is directory.
- /// </summary>
- /// <value><c>true</c> if this instance is directory; otherwise, <c>false</c>.</value>
- public bool IsDirectory { get; set; }
- public bool IsHidden { get; set; }
- public bool IsReadOnly { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
deleted file mode 100644
index ea6b04824..000000000
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ /dev/null
@@ -1,450 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Interface IFileSystem
- /// </summary>
- public interface IFileSystem
- {
- void AddShortcutHandler(IShortcutHandler handler);
-
- /// <summary>
- /// Determines whether the specified filename is shortcut.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
- bool IsShortcut(string filename);
-
- /// <summary>
- /// Resolves the shortcut.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.String.</returns>
- string ResolveShortcut(string filename);
-
- /// <summary>
- /// Creates the shortcut.
- /// </summary>
- /// <param name="shortcutPath">The shortcut path.</param>
- /// <param name="target">The target.</param>
- void CreateShortcut(string shortcutPath, string target);
-
- /// <summary>
- /// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
- /// </summary>
- /// <param name="path">A path to a file or directory.</param>
- /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
- /// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
- /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks>
- FileSystemMetadata GetFileSystemInfo(string path);
-
- /// <summary>
- /// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
- /// </summary>
- /// <param name="path">A path to a file.</param>
- /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
- /// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
- /// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> property will both be set to false.</para>
- /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
- FileSystemMetadata GetFileInfo(string path);
-
- /// <summary>
- /// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
- /// </summary>
- /// <param name="path">A path to a directory.</param>
- /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
- /// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object's
- /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetadata.Exists"/> property will be set to false.</para>
- /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
- FileSystemMetadata GetDirectoryInfo(string path);
-
- /// <summary>
- /// Gets the valid filename.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.String.</returns>
- string GetValidFilename(string filename);
-
- /// <summary>
- /// Gets the creation time UTC.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>DateTime.</returns>
- DateTime GetCreationTimeUtc(FileSystemMetadata info);
-
- /// <summary>
- /// Gets the creation time UTC.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>DateTime.</returns>
- DateTime GetCreationTimeUtc(string path);
-
- /// <summary>
- /// Gets the last write time UTC.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>DateTime.</returns>
- DateTime GetLastWriteTimeUtc(FileSystemMetadata info);
-
- /// <summary>
- /// Gets the last write time UTC.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>DateTime.</returns>
- DateTime GetLastWriteTimeUtc(string path);
-
- /// <summary>
- /// Gets the file stream.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="mode">The mode.</param>
- /// <param name="access">The access.</param>
- /// <param name="share">The share.</param>
- /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
- /// <returns>FileStream.</returns>
- Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false);
-
- Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions);
-
- /// <summary>
- /// Opens the read.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>Stream.</returns>
- Stream OpenRead(String path);
-
- /// <summary>
- /// Swaps the files.
- /// </summary>
- /// <param name="file1">The file1.</param>
- /// <param name="file2">The file2.</param>
- void SwapFiles(string file1, string file2);
-
- bool AreEqual(string path1, string path2);
-
- /// <summary>
- /// Determines whether [contains sub path] [the specified parent path].
- /// </summary>
- /// <param name="parentPath">The parent path.</param>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns>
- bool ContainsSubPath(string parentPath, string path);
-
- /// <summary>
- /// Determines whether [is root path] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is root path] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsRootPath(string path);
-
- /// <summary>
- /// Normalizes the path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- string NormalizePath(string path);
-
- string GetDirectoryName(string path);
-
- /// <summary>
- /// Gets the file name without extension.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <returns>System.String.</returns>
- string GetFileNameWithoutExtension(FileSystemMetadata info);
-
- /// <summary>
- /// Gets the file name without extension.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- string GetFileNameWithoutExtension(string path);
-
- /// <summary>
- /// Determines whether [is path file] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsPathFile(string path);
-
- /// <summary>
- /// Deletes the file.
- /// </summary>
- /// <param name="path">The path.</param>
- void DeleteFile(string path);
-
- /// <summary>
- /// Deletes the directory.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- void DeleteDirectory(string path, bool recursive);
-
- /// <summary>
- /// Gets the directories.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>IEnumerable&lt;DirectoryInfo&gt;.</returns>
- IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false);
-
- /// <summary>
- /// Gets the files.
- /// </summary>
- IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false);
-
- IEnumerable<FileSystemMetadata> GetFiles(string path, string [] extensions, bool enableCaseSensitiveExtensions, bool recursive);
-
- /// <summary>
- /// Gets the file system entries.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>IEnumerable&lt;FileSystemMetadata&gt;.</returns>
- IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false);
-
- /// <summary>
- /// Creates the directory.
- /// </summary>
- /// <param name="path">The path.</param>
- void CreateDirectory(string path);
-
- /// <summary>
- /// Copies the file.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
- void CopyFile(string source, string target, bool overwrite);
-
- /// <summary>
- /// Moves the file.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- void MoveFile(string source, string target);
-
- /// <summary>
- /// Moves the directory.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- void MoveDirectory(string source, string target);
-
- /// <summary>
- /// Directories the exists.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool DirectoryExists(string path);
-
- /// <summary>
- /// Files the exists.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool FileExists(string path);
-
- /// <summary>
- /// Reads all text.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.String.</returns>
- string ReadAllText(string path);
-
- byte[] ReadAllBytes(string path);
-
- void WriteAllBytes(string path, byte[] bytes);
-
- /// <summary>
- /// Writes all text.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="text">The text.</param>
- void WriteAllText(string path, string text);
-
- /// <summary>
- /// Writes all text.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="text">The text.</param>
- /// <param name="encoding">The encoding.</param>
- void WriteAllText(string path, string text, Encoding encoding);
-
- /// <summary>
- /// Reads all text.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="encoding">The encoding.</param>
- /// <returns>System.String.</returns>
- string ReadAllText(string path, Encoding encoding);
-
- string[] ReadAllLines(string path);
-
- void WriteAllLines(string path, IEnumerable<string> lines);
-
- /// <summary>
- /// Gets the directory paths.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>IEnumerable&lt;System.String&gt;.</returns>
- IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false);
-
- /// <summary>
- /// Gets the file paths.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>IEnumerable&lt;System.String&gt;.</returns>
- IEnumerable<string> GetFilePaths(string path, bool recursive = false);
- IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive);
-
- /// <summary>
- /// Gets the file system entry paths.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <returns>IEnumerable&lt;System.String&gt;.</returns>
- IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false);
-
- void SetHidden(string path, bool isHidden);
- void SetReadOnly(string path, bool readOnly);
- void SetAttributes(string path, bool isHidden, bool readOnly);
-
- char DirectorySeparatorChar { get; }
-
- string GetFullPath(string path);
-
- List<FileSystemMetadata> GetDrives();
-
- void SetExecutable(string path);
- }
-
- public enum FileOpenMode
- {
- //
- // Summary:
- // Specifies that the operating system should create a new file. This requires System.Security.Permissions.FileIOPermissionAccess.Write
- // permission. If the file already exists, an System.IO.IOException exception is
- // thrown.
- CreateNew = 1,
- //
- // Summary:
- // Specifies that the operating system should create a new file. If the file already
- // exists, it will be overwritten. This requires System.Security.Permissions.FileIOPermissionAccess.Write
- // permission. FileMode.Create is equivalent to requesting that if the file does
- // not exist, use System.IO.FileMode.CreateNew; otherwise, use System.IO.FileMode.Truncate.
- // If the file already exists but is a hidden file, an System.UnauthorizedAccessException
- // exception is thrown.
- Create = 2,
- //
- // Summary:
- // Specifies that the operating system should open an existing file. The ability
- // to open the file is dependent on the value specified by the System.IO.FileAccess
- // enumeration. A System.IO.FileNotFoundException exception is thrown if the file
- // does not exist.
- Open = 3,
- //
- // Summary:
- // Specifies that the operating system should open a file if it exists; otherwise,
- // a new file should be created. If the file is opened with FileAccess.Read, System.Security.Permissions.FileIOPermissionAccess.Read
- // permission is required. If the file access is FileAccess.Write, System.Security.Permissions.FileIOPermissionAccess.Write
- // permission is required. If the file is opened with FileAccess.ReadWrite, both
- // System.Security.Permissions.FileIOPermissionAccess.Read and System.Security.Permissions.FileIOPermissionAccess.Write
- // permissions are required.
- OpenOrCreate = 4
- }
-
- public enum FileAccessMode
- {
- //
- // Summary:
- // Read access to the file. Data can be read from the file. Combine with Write for
- // read/write access.
- Read = 1,
- //
- // Summary:
- // Write access to the file. Data can be written to the file. Combine with Read
- // for read/write access.
- Write = 2
- }
-
- public enum FileShareMode
- {
- //
- // Summary:
- // Declines sharing of the current file. Any request to open the file (by this process
- // or another process) will fail until the file is closed.
- None = 0,
- //
- // Summary:
- // Allows subsequent opening of the file for reading. If this flag is not specified,
- // any request to open the file for reading (by this process or another process)
- // will fail until the file is closed. However, even if this flag is specified,
- // additional permissions might still be needed to access the file.
- Read = 1,
- //
- // Summary:
- // Allows subsequent opening of the file for writing. If this flag is not specified,
- // any request to open the file for writing (by this process or another process)
- // will fail until the file is closed. However, even if this flag is specified,
- // additional permissions might still be needed to access the file.
- Write = 2,
- //
- // Summary:
- // Allows subsequent opening of the file for reading or writing. If this flag is
- // not specified, any request to open the file for reading or writing (by this process
- // or another process) will fail until the file is closed. However, even if this
- // flag is specified, additional permissions might still be needed to access the
- // file.
- ReadWrite = 3
- }
-
- //
- // Summary:
- // Represents advanced options for creating a System.IO.FileStream object.
- [Flags]
- public enum FileOpenOptions
- {
- //
- // Summary:
- // Indicates that the system should write through any intermediate cache and go
- // directly to disk.
- WriteThrough = int.MinValue,
- //
- // Summary:
- // Indicates that no additional options should be used when creating a System.IO.FileStream
- // object.
- None = 0,
- //
- // Summary:
- // Indicates that a file is encrypted and can be decrypted only by using the same
- // user account used for encryption.
- Encrypted = 16384,
- //
- // Summary:
- // Indicates that a file is automatically deleted when it is no longer in use.
- DeleteOnClose = 67108864,
- //
- // Summary:
- // Indicates that the file is to be accessed sequentially from beginning to end.
- // The system can use this as a hint to optimize file caching. If an application
- // moves the file pointer for random access, optimum caching may not occur; however,
- // correct operation is still guaranteed.
- SequentialScan = 134217728,
- //
- // Summary:
- // Indicates that the file is accessed randomly. The system can use this as a hint
- // to optimize file caching.
- RandomAccess = 268435456,
- //
- // Summary:
- // Indicates that a file can be used for asynchronous reading and writing.
- Asynchronous = 1073741824
- }
-}
diff --git a/MediaBrowser.Model/IO/IIsoManager.cs b/MediaBrowser.Model/IO/IIsoManager.cs
deleted file mode 100644
index 92c4d5aee..000000000
--- a/MediaBrowser.Model/IO/IIsoManager.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.IO
-{
- public interface IIsoManager : IDisposable
- {
- /// <summary>
- /// Mounts the specified iso path.
- /// </summary>
- /// <param name="isoPath">The iso path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IsoMount.</returns>
- /// <exception cref="ArgumentNullException">isoPath</exception>
- /// <exception cref="IOException">Unable to create mount.</exception>
- Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken);
-
- /// <summary>
- /// Determines whether this instance can mount the specified path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns>
- bool CanMount(string path);
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="mounters">The mounters.</param>
- void AddParts(IEnumerable<IIsoMounter> mounters);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/IO/IIsoMount.cs b/MediaBrowser.Model/IO/IIsoMount.cs
deleted file mode 100644
index 4f8f8b5d2..000000000
--- a/MediaBrowser.Model/IO/IIsoMount.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Interface IIsoMount
- /// </summary>
- public interface IIsoMount : IDisposable
- {
- /// <summary>
- /// Gets or sets the iso path.
- /// </summary>
- /// <value>The iso path.</value>
- string IsoPath { get; }
-
- /// <summary>
- /// Gets the mounted path.
- /// </summary>
- /// <value>The mounted path.</value>
- string MountedPath { get; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/IO/IIsoMounter.cs b/MediaBrowser.Model/IO/IIsoMounter.cs
deleted file mode 100644
index 6684e7eff..000000000
--- a/MediaBrowser.Model/IO/IIsoMounter.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.IO
-{
- public interface IIsoMounter : IDisposable
- {
- /// <summary>
- /// Mounts the specified iso path.
- /// </summary>
- /// <param name="isoPath">The iso path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IsoMount.</returns>
- /// <exception cref="ArgumentNullException">isoPath</exception>
- /// <exception cref="IOException">Unable to create mount.</exception>
- Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken);
-
- /// <summary>
- /// Determines whether this instance can mount the specified path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns>
- bool CanMount(string path);
-
- /// <summary>
- /// Gets a value indicating whether [requires installation].
- /// </summary>
- /// <value><c>true</c> if [requires installation]; otherwise, <c>false</c>.</value>
- bool RequiresInstallation { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is installed.
- /// </summary>
- /// <value><c>true</c> if this instance is installed; otherwise, <c>false</c>.</value>
- bool IsInstalled { get; }
-
- /// <summary>
- /// Installs this instance.
- /// </summary>
- /// <returns>Task.</returns>
- Task Install(CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
- }
-}
diff --git a/MediaBrowser.Model/IO/IMemoryStreamFactory.cs b/MediaBrowser.Model/IO/IMemoryStreamFactory.cs
deleted file mode 100644
index f4f174643..000000000
--- a/MediaBrowser.Model/IO/IMemoryStreamFactory.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.IO;
-
-namespace MediaBrowser.Model.IO
-{
- public interface IMemoryStreamFactory
- {
- MemoryStream CreateNew();
- MemoryStream CreateNew(int capacity);
- MemoryStream CreateNew(byte[] buffer);
- bool TryGetBuffer(MemoryStream stream, out byte[] buffer);
- }
-}
diff --git a/MediaBrowser.Model/IO/IShortcutHandler.cs b/MediaBrowser.Model/IO/IShortcutHandler.cs
deleted file mode 100644
index 16255e51f..000000000
--- a/MediaBrowser.Model/IO/IShortcutHandler.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-
-namespace MediaBrowser.Model.IO
-{
- public interface IShortcutHandler
- {
- /// <summary>
- /// Gets the extension.
- /// </summary>
- /// <value>The extension.</value>
- string Extension { get; }
- /// <summary>
- /// Resolves the specified shortcut path.
- /// </summary>
- /// <param name="shortcutPath">The shortcut path.</param>
- /// <returns>System.String.</returns>
- string Resolve(string shortcutPath);
- /// <summary>
- /// Creates the specified shortcut path.
- /// </summary>
- /// <param name="shortcutPath">The shortcut path.</param>
- /// <param name="targetPath">The target path.</param>
- /// <returns>System.String.</returns>
- void Create(string shortcutPath, string targetPath);
- }
-}
diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs
deleted file mode 100644
index 4ebcba9d8..000000000
--- a/MediaBrowser.Model/IO/IZipClient.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System.IO;
-
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Interface IZipClient
- /// </summary>
- public interface IZipClient
- {
- /// <summary>
- /// Extracts all.
- /// </summary>
- /// <param name="sourceFile">The source file.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles);
-
- void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles);
- void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName);
-
- /// <summary>
- /// Extracts all from zip.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from7z.
- /// </summary>
- /// <param name="sourceFile">The source file.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from7z.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from tar.
- /// </summary>
- /// <param name="sourceFile">The source file.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from tar.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from rar.
- /// </summary>
- /// <param name="sourceFile">The source file.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles);
-
- /// <summary>
- /// Extracts all from rar.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="targetPath">The target path.</param>
- /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
- void ExtractAllFromRar(Stream source, string targetPath, bool overwriteExistingFiles);
- }
-}
diff --git a/MediaBrowser.Model/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/StreamDefaults.cs
deleted file mode 100644
index 1e99ff4b5..000000000
--- a/MediaBrowser.Model/IO/StreamDefaults.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.IO
-{
- /// <summary>
- /// Class StreamDefaults
- /// </summary>
- public static class StreamDefaults
- {
- /// <summary>
- /// The default copy to buffer size
- /// </summary>
- public const int DefaultCopyToBufferSize = 81920;
-
- /// <summary>
- /// The default file stream buffer size
- /// </summary>
- public const int DefaultFileStreamBufferSize = 81920;
- }
-}
diff --git a/MediaBrowser.Model/Library/PlayAccess.cs b/MediaBrowser.Model/Library/PlayAccess.cs
deleted file mode 100644
index 6ec845fc7..000000000
--- a/MediaBrowser.Model/Library/PlayAccess.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Library
-{
- public enum PlayAccess
- {
- Full = 0,
- None = 1
- }
-}
diff --git a/MediaBrowser.Model/Library/UserViewQuery.cs b/MediaBrowser.Model/Library/UserViewQuery.cs
deleted file mode 100644
index 3915d7644..000000000
--- a/MediaBrowser.Model/Library/UserViewQuery.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-
-namespace MediaBrowser.Model.Library
-{
- public class UserViewQuery
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [include external content].
- /// </summary>
- /// <value><c>true</c> if [include external content]; otherwise, <c>false</c>.</value>
- public bool IncludeExternalContent { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [include hidden].
- /// </summary>
- /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
- public bool IncludeHidden { get; set; }
-
- public string[] PresetViews { get; set; }
-
- public UserViewQuery()
- {
- IncludeExternalContent = true;
- PresetViews = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
deleted file mode 100644
index e4d32ca4a..000000000
--- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class BaseTimerInfoDto : IHasServerId
- {
- /// <summary>
- /// Id of the recording.
- /// </summary>
- public string Id { get; set; }
-
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the external identifier.
- /// </summary>
- /// <value>The external identifier.</value>
- public string ExternalId { get; set; }
-
- /// <summary>
- /// ChannelId of the recording.
- /// </summary>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the external channel identifier.
- /// </summary>
- /// <value>The external channel identifier.</value>
- public string ExternalChannelId { get; set; }
-
- /// <summary>
- /// ChannelName of the recording.
- /// </summary>
- public string ChannelName { get; set; }
-
- public string ChannelPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- /// <summary>
- /// Gets or sets the external program identifier.
- /// </summary>
- /// <value>The external program identifier.</value>
- public string ExternalProgramId { get; set; }
-
- /// <summary>
- /// Name of the recording.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Description of the recording.
- /// </summary>
- public string Overview { get; set; }
-
- /// <summary>
- /// The start date of the recording, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// The end date of the recording, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the service.
- /// </summary>
- /// <value>The name of the service.</value>
- public string ServiceName { get; set; }
-
- /// <summary>
- /// Gets or sets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public int Priority { get; set; }
-
- /// <summary>
- /// Gets or sets the pre padding seconds.
- /// </summary>
- /// <value>The pre padding seconds.</value>
- public int PrePaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets the post padding seconds.
- /// </summary>
- /// <value>The post padding seconds.</value>
- public int PostPaddingSeconds { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is pre padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value>
- public bool IsPrePaddingRequired { get; set; }
-
- /// <summary>
- /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
- /// </summary>
- /// <value>The parent backdrop item id.</value>
- public string ParentBackdropItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent backdrop image tags.
- /// </summary>
- /// <value>The parent backdrop image tags.</value>
- public string[] ParentBackdropImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is post padding required.
- /// </summary>
- /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
- public bool IsPostPaddingRequired { get; set; }
- public KeepUntil KeepUntil { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
deleted file mode 100644
index 67e3d44da..000000000
--- a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Library;
-using System.Collections.Generic;
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class ChannelInfoDto
- /// </summary>
- [DebuggerDisplay("Name = {Name}, Number = {Number}")]
- public class ChannelInfoDto : IItemDto, IHasServerId
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the external identifier.
- /// </summary>
- /// <value>The external identifier.</value>
- public string ExternalId { get; set; }
-
- /// <summary>
- /// Gets or sets the media sources.
- /// </summary>
- /// <value>The media sources.</value>
- public MediaSourceInfo[] MediaSources { get; set; }
-
- /// <summary>
- /// Gets or sets the image tags.
- /// </summary>
- /// <value>The image tags.</value>
- public Dictionary<ImageType, string> ImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the number.
- /// </summary>
- /// <value>The number.</value>
- public string Number { get; set; }
-
- /// <summary>
- /// Gets or sets the play access.
- /// </summary>
- /// <value>The play access.</value>
- public PlayAccess PlayAccess { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the service.
- /// </summary>
- /// <value>The name of the service.</value>
- public string ServiceName { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType ChannelType { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- public string MediaType { get; set; }
-
- /// <summary>
- /// Gets or sets the user data.
- /// </summary>
- /// <value>The user data.</value>
- public UserItemDataDto UserData { get; set; }
-
- /// <summary>
- /// Gets or sets the now playing program.
- /// </summary>
- /// <value>The now playing program.</value>
- public BaseItemDto CurrentProgram { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image aspect ratio, after image enhancements.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- public double? PrimaryImageAspectRatio { get; set; }
-
- /// <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); }
- }
-
- public ChannelInfoDto()
- {
- ImageTags = new Dictionary<ImageType, string>();
- MediaSources = new MediaSourceInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/ChannelType.cs b/MediaBrowser.Model/LiveTv/ChannelType.cs
deleted file mode 100644
index bca16f839..000000000
--- a/MediaBrowser.Model/LiveTv/ChannelType.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Enum ChannelType
- /// </summary>
- public enum ChannelType
- {
- /// <summary>
- /// The TV
- /// </summary>
- TV,
-
- /// <summary>
- /// The radio
- /// </summary>
- Radio
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/DayPattern.cs b/MediaBrowser.Model/LiveTv/DayPattern.cs
deleted file mode 100644
index 8251795dc..000000000
--- a/MediaBrowser.Model/LiveTv/DayPattern.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public enum DayPattern
- {
- Daily,
- Weekdays,
- Weekends
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/GuideInfo.cs b/MediaBrowser.Model/LiveTv/GuideInfo.cs
deleted file mode 100644
index c21f6d871..000000000
--- a/MediaBrowser.Model/LiveTv/GuideInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class GuideInfo
- {
- /// <summary>
- /// Gets or sets the start date.
- /// </summary>
- /// <value>The start date.</value>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// Gets or sets the end date.
- /// </summary>
- /// <value>The end date.</value>
- public DateTime EndDate { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
deleted file mode 100644
index 4505b80a0..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class ChannelQuery.
- /// </summary>
- public class LiveTvChannelQuery
- {
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType? ChannelType { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
- public bool? IsFavorite { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is liked.
- /// </summary>
- /// <value><c>null</c> if [is liked] contains no value, <c>true</c> if [is liked]; otherwise, <c>false</c>.</value>
- public bool? IsLiked { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is disliked.
- /// </summary>
- /// <value><c>null</c> if [is disliked] contains no value, <c>true</c> if [is disliked]; otherwise, <c>false</c>.</value>
- public bool? IsDisliked { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable favorite sorting].
- /// </summary>
- /// <value><c>true</c> if [enable favorite sorting]; otherwise, <c>false</c>.</value>
- public bool EnableFavoriteSorting { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { 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>
- /// Gets or sets a value indicating whether [add current program].
- /// </summary>
- /// <value><c>true</c> if [add current program]; otherwise, <c>false</c>.</value>
- public bool AddCurrentProgram { get; set; }
- public bool EnableUserData { get; set; }
-
- /// <summary>
- /// Used to specific whether to return news or not
- /// </summary>
- /// <remarks>If set to null, all programs will be returned</remarks>
- public bool? IsNews { get; set; }
-
- /// <summary>
- /// Used to specific whether to return movies or not
- /// </summary>
- /// <remarks>If set to null, all programs will be returned</remarks>
- public bool? IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value>
- public bool? IsKids { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value>
- public bool? IsSports { get; set; }
- public bool? IsSeries { get; set; }
-
- public string[] SortBy { get; set; }
-
- /// <summary>
- /// The sort order to return results with
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder? SortOrder { get; set; }
-
- public LiveTvChannelQuery()
- {
- EnableUserData = true;
- SortBy = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
deleted file mode 100644
index 4620fbf0c..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class LiveTvInfo
- {
- /// <summary>
- /// Gets or sets the services.
- /// </summary>
- /// <value>The services.</value>
- public LiveTvServiceInfo[] Services { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is enabled.
- /// </summary>
- /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
- public bool IsEnabled { get; set; }
-
- /// <summary>
- /// Gets or sets the enabled users.
- /// </summary>
- /// <value>The enabled users.</value>
- public string[] EnabledUsers { get; set; }
-
- public LiveTvInfo()
- {
- Services = new LiveTvServiceInfo[] { };
- EnabledUsers = new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
deleted file mode 100644
index 75edf05aa..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class LiveTvOptions
- {
- public int? GuideDays { get; set; }
- public string RecordingPath { get; set; }
- public string MovieRecordingPath { get; set; }
- public string SeriesRecordingPath { get; set; }
- public bool EnableRecordingEncoding { get; set; }
- public string RecordingEncodingFormat { get; set; }
- public bool EnableRecordingSubfolders { get; set; }
- public bool EnableOriginalAudioWithEncodedRecordings { get; set; }
-
- public TunerHostInfo[] TunerHosts { get; set; }
- public ListingsProviderInfo[] ListingProviders { get; set; }
-
- public int PrePaddingSeconds { get; set; }
- public int PostPaddingSeconds { get; set; }
-
- public string[] MediaLocationsCreated { get; set; }
-
- public string RecordingPostProcessor { get; set; }
- public string RecordingPostProcessorArguments { get; set; }
-
- public LiveTvOptions()
- {
- TunerHosts = new TunerHostInfo[] { };
- ListingProviders = new ListingsProviderInfo[] { };
- MediaLocationsCreated = new string[] { };
- RecordingEncodingFormat = "mkv";
- RecordingPostProcessorArguments = "\"{path}\"";
- }
- }
-
- public class TunerHostInfo
- {
- public string Id { get; set; }
- public string Url { get; set; }
- public string Type { get; set; }
- public string DeviceId { get; set; }
- public string FriendlyName { get; set; }
- public bool ImportFavoritesOnly { get; set; }
- public bool AllowHWTranscoding { get; set; }
- public bool EnableStreamLooping { get; set; }
- public bool EnableNewHdhrChannelIds { get; set; }
- public string Source { get; set; }
- public int TunerCount { get; set; }
-
- public TunerHostInfo()
- {
- AllowHWTranscoding = true;
- }
- }
-
- public class ListingsProviderInfo
- {
- public string Id { get; set; }
- public string Type { get; set; }
- public string Username { get; set; }
- public string Password { get; set; }
- public string ListingsId { get; set; }
- public string ZipCode { get; set; }
- public string Country { get; set; }
- public string Path { get; set; }
-
- public string[] EnabledTuners { get; set; }
- public bool EnableAllTuners { get; set; }
- public string[] NewsCategories { get; set; }
- public string[] SportsCategories { get; set; }
- public string[] KidsCategories { get; set; }
- public string[] MovieCategories { get; set; }
- public NameValuePair[] ChannelMappings { get; set; }
- public string MoviePrefix { get; set; }
- public bool EnableNewProgramIds { get; set; }
- public string PreferredLanguage { get; set; }
-
- public ListingsProviderInfo()
- {
- NewsCategories = new string[] { "news", "journalism", "documentary", "current affairs" };
- SportsCategories = new string[] { "sports", "basketball", "baseball", "football" };
- KidsCategories = new string[] { "kids", "family", "children", "childrens", "disney" };
- MovieCategories = new string[] { "movie" };
- EnabledTuners = new string[] { };
- EnableAllTuners = true;
- ChannelMappings = new NameValuePair[] {};
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
deleted file mode 100644
index 09ec4b931..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class ServiceInfo
- /// </summary>
- public class LiveTvServiceInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the home page URL.
- /// </summary>
- /// <value>The home page URL.</value>
- public string HomePageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvServiceStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the status message.
- /// </summary>
- /// <value>The status message.</value>
- public string StatusMessage { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- public bool HasUpdateAvailable { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is visible.
- /// </summary>
- /// <value><c>true</c> if this instance is visible; otherwise, <c>false</c>.</value>
- public bool IsVisible { get; set; }
-
- public LiveTvTunerInfoDto[] Tuners { get; set; }
-
- public LiveTvServiceInfo()
- {
- Tuners = new LiveTvTunerInfoDto[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
deleted file mode 100644
index 20fe84500..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public enum LiveTvServiceStatus
- {
- Ok = 0,
- Unavailable = 1
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs
deleted file mode 100644
index 937cef057..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class LiveTvTunerInfoDto
- {
- /// <summary>
- /// Gets or sets the type of the source.
- /// </summary>
- /// <value>The type of the source.</value>
- public string SourceType { get; set; }
-
- /// <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 URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvTunerStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the channel.
- /// </summary>
- /// <value>The name of the channel.</value>
- public string ChannelName { get; set; }
-
- /// <summary>
- /// Gets or sets the recording identifier.
- /// </summary>
- /// <value>The recording identifier.</value>
- public string RecordingId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the program.
- /// </summary>
- /// <value>The name of the program.</value>
- public string ProgramName { get; set; }
-
- /// <summary>
- /// Gets or sets the clients.
- /// </summary>
- /// <value>The clients.</value>
- public string[] Clients { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can reset.
- /// </summary>
- /// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value>
- public bool CanReset { get; set; }
-
- public LiveTvTunerInfoDto()
- {
- Clients = new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
deleted file mode 100644
index 055199fca..000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public enum LiveTvTunerStatus
- {
- Available = 0,
- Disabled = 1,
- RecordingTv = 2,
- LiveTv = 3
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/ProgramAudio.cs b/MediaBrowser.Model/LiveTv/ProgramAudio.cs
deleted file mode 100644
index 9a272492c..000000000
--- a/MediaBrowser.Model/LiveTv/ProgramAudio.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public enum ProgramAudio
- {
- Mono,
- Stereo,
- Dolby,
- DolbyDigital,
- Thx,
- Atmos
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs
deleted file mode 100644
index ec3f8ad67..000000000
--- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class ProgramQuery.
- /// </summary>
- public class ProgramQuery
- {
- public ProgramQuery()
- {
- ChannelIds = new string[] { };
- OrderBy = new Tuple<string, SortOrder>[] { };
- Genres = new string[] { };
- GenreIds = new string[] { };
- EnableTotalRecordCount = true;
- EnableUserData = true;
- }
-
- public bool EnableTotalRecordCount { get; set; }
- public bool EnableUserData { get; set; }
-
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- public bool? EnableImages { get; set; }
- public int? ImageTypeLimit { get; set; }
- public ImageType[] EnableImageTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the channel ids.
- /// </summary>
- /// <value>The channel ids.</value>
- public string[] ChannelIds { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- public string SeriesTimerId { get; set; }
- public string Name { get; set; }
-
- /// <summary>
- /// The earliest date for which a program starts to return
- /// </summary>
- public DateTime? MinStartDate { get; set; }
-
- /// <summary>
- /// The latest date for which a program starts to return
- /// </summary>
- public DateTime? MaxStartDate { get; set; }
-
- /// <summary>
- /// The earliest date for which a program ends to return
- /// </summary>
- public DateTime? MinEndDate { get; set; }
-
- /// <summary>
- /// The latest date for which a program ends to return
- /// </summary>
- public DateTime? MaxEndDate { get; set; }
-
- /// <summary>
- /// Used to specific whether to return news or not
- /// </summary>
- /// <remarks>If set to null, all programs will be returned</remarks>
- public bool? IsNews { get; set; }
-
- /// <summary>
- /// Used to specific whether to return movies or not
- /// </summary>
- /// <remarks>If set to null, all programs will be returned</remarks>
- public bool? IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value>
- public bool? IsKids { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value>
- public bool? IsSports { get; set; }
-
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- public int? StartIndex { get; set; }
- public bool? IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has aired.
- /// </summary>
- /// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value>
- public bool? HasAired { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- public int? Limit { get; set; }
-
- public Tuple<string, SortOrder>[] OrderBy { get; set; }
-
- /// <summary>
- /// Limit results to items containing specific genres
- /// </summary>
- /// <value>The genres.</value>
- public string[] GenreIds { get; set; }
- public string[] Genres { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs
deleted file mode 100644
index 3d137256e..000000000
--- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class RecommendedProgramQuery
- {
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- public bool? EnableImages { get; set; }
- public int? ImageTypeLimit { get; set; }
- public ImageType[] EnableImageTypes { get; set; }
- public string[] GenreIds { get; set; }
-
- public bool EnableTotalRecordCount { get; set; }
-
- public RecommendedProgramQuery()
- {
- EnableTotalRecordCount = true;
- GenreIds = new string[] { };
- }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is airing.
- /// </summary>
- /// <value><c>true</c> if this instance is airing; otherwise, <c>false</c>.</value>
- public bool? IsAiring { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has aired.
- /// </summary>
- /// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value>
- public bool? HasAired { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value>
- public bool? IsNews { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value>
- public bool? IsMovie { get; set; }
- public bool? IsSeries { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value>
- public bool? IsKids { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value>
- public bool? IsSports { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/RecordingGroupQuery.cs b/MediaBrowser.Model/LiveTv/RecordingGroupQuery.cs
deleted file mode 100644
index 8c20e7f3f..000000000
--- a/MediaBrowser.Model/LiveTv/RecordingGroupQuery.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public class RecordingGroupQuery
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs
deleted file mode 100644
index 9e9aafc44..000000000
--- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class RecordingQuery.
- /// </summary>
- public class RecordingQuery
- {
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the group identifier.
- /// </summary>
- /// <value>The group identifier.</value>
- public string GroupId { 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>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public RecordingStatus? Status { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is in progress.
- /// </summary>
- /// <value><c>null</c> if [is in progress] contains no value, <c>true</c> if [is in progress]; otherwise, <c>false</c>.</value>
- public bool? IsInProgress { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- public bool? EnableImages { get; set; }
- public bool? IsLibraryItem { get; set; }
- public bool? IsNews { get; set; }
- public bool? IsMovie { get; set; }
- public bool? IsSeries { get; set; }
- public bool? IsKids { get; set; }
- public bool? IsSports { get; set; }
- public int? ImageTypeLimit { get; set; }
- public ImageType[] EnableImageTypes { get; set; }
-
- public bool EnableTotalRecordCount { get; set; }
-
- public RecordingQuery()
- {
- EnableTotalRecordCount = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs
deleted file mode 100644
index 496e6f421..000000000
--- a/MediaBrowser.Model/LiveTv/RecordingStatus.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.LiveTv
-{
- public enum RecordingStatus
- {
- New,
- InProgress,
- Completed,
- Cancelled,
- ConflictedOk,
- ConflictedNotOk,
- Error
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
deleted file mode 100644
index 743caa97e..000000000
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.LiveTv
-{
- /// <summary>
- /// Class SeriesTimerInfoDto.
- /// </summary>
- [DebuggerDisplay("Name = {Name}")]
- public class SeriesTimerInfoDto : BaseTimerInfoDto
- {
- public SeriesTimerInfoDto()
- {
- ImageTags = new Dictionary<ImageType, string>();
- Days = new DayOfWeek[] { };
- Type = "SeriesTimer";
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record any time].
- /// </summary>
- /// <value><c>true</c> if [record any time]; otherwise, <c>false</c>.</value>
- public bool RecordAnyTime { get; set; }
-
- public bool SkipEpisodesInLibrary { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record any channel].
- /// </summary>
- /// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value>
- public bool RecordAnyChannel { get; set; }
-
- public int KeepUpTo { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [record new only].
- /// </summary>
- /// <value><c>true</c> if [record new only]; otherwise, <c>false</c>.</value>
- public bool RecordNewOnly { get; set; }
-
- /// <summary>
- /// Gets or sets the days.
- /// </summary>
- /// <value>The days.</value>
- public DayOfWeek[] Days { get; set; }
-
- /// <summary>
- /// Gets or sets the day pattern.
- /// </summary>
- /// <value>The day pattern.</value>
- public DayPattern? DayPattern { get; set; }
-
- /// <summary>
- /// Gets or sets the image tags.
- /// </summary>
- /// <value>The image tags.</value>
- public Dictionary<ImageType, string> ImageTags { get; set; }
-
- /// <summary>
- /// Gets or sets the parent thumb item id.
- /// </summary>
- /// <value>The parent thumb item id.</value>
- public string ParentThumbItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent thumb image tag.
- /// </summary>
- /// <value>The parent thumb image tag.</value>
- public string ParentThumbImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the parent primary image item identifier.
- /// </summary>
- /// <value>The parent primary image item identifier.</value>
- public string ParentPrimaryImageItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent primary image tag.
- /// </summary>
- /// <value>The parent primary image tag.</value>
- public string ParentPrimaryImageTag { get; set; }
- }
-
- public enum KeepUntil
- {
- UntilDeleted,
- UntilSpaceNeeded,
- UntilWatched,
- UntilDate
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
deleted file mode 100644
index 95260cc0e..000000000
--- a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class SeriesTimerQuery
- {
- /// <summary>
- /// Gets or sets the sort by - SortName, Priority
- /// </summary>
- /// <value>The sort by.</value>
- public string SortBy { get; set; }
-
- /// <summary>
- /// Gets or sets the sort order.
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder SortOrder { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
deleted file mode 100644
index d1aa3118f..000000000
--- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.LiveTv
-{
- public class TimerInfoDto : BaseTimerInfoDto
- {
- public TimerInfoDto()
- {
- Type = "Timer";
- }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public RecordingStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the external series timer identifier.
- /// </summary>
- /// <value>The external series timer identifier.</value>
- public string ExternalSeriesTimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- public long? RunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the program information.
- /// </summary>
- /// <value>The program information.</value>
- public BaseItemDto ProgramInfo { get; set; }
-
- }
-}
diff --git a/MediaBrowser.Model/LiveTv/TimerQuery.cs b/MediaBrowser.Model/LiveTv/TimerQuery.cs
deleted file mode 100644
index c6202680c..000000000
--- a/MediaBrowser.Model/LiveTv/TimerQuery.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace MediaBrowser.Model.LiveTv
-{
- public class TimerQuery
- {
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- public bool? IsActive { get; set; }
-
- public bool? IsScheduled { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Logging/IConsoleLogger.cs b/MediaBrowser.Model/Logging/IConsoleLogger.cs
deleted file mode 100644
index a8c282d65..000000000
--- a/MediaBrowser.Model/Logging/IConsoleLogger.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Model.Logging
-{
- public interface IConsoleLogger
- {
- void WriteLine(string message);
- }
-}
diff --git a/MediaBrowser.Model/Logging/ILogManager.cs b/MediaBrowser.Model/Logging/ILogManager.cs
deleted file mode 100644
index 218f13eb4..000000000
--- a/MediaBrowser.Model/Logging/ILogManager.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Logging
-{
- /// <summary>
- /// Interface ILogManager
- /// </summary>
- public interface ILogManager
- {
- /// <summary>
- /// Gets or sets the log level.
- /// </summary>
- /// <value>The log level.</value>
- LogSeverity LogSeverity { get; set; }
-
- /// <summary>
- /// Gets or sets the exception message prefix.
- /// </summary>
- /// <value>The exception message prefix.</value>
- string ExceptionMessagePrefix { get; set; }
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>ILogger.</returns>
- ILogger GetLogger(string name);
-
- /// <summary>
- /// Reloads the logger.
- /// </summary>
- void ReloadLogger(LogSeverity severity);
-
- /// <summary>
- /// Occurs when [logger loaded].
- /// </summary>
- event EventHandler LoggerLoaded;
-
- /// <summary>
- /// Flushes this instance.
- /// </summary>
- void Flush();
-
- /// <summary>
- /// Adds the console output.
- /// </summary>
- void AddConsoleOutput();
-
- /// <summary>
- /// Removes the console output.
- /// </summary>
- void RemoveConsoleOutput();
- }
-}
diff --git a/MediaBrowser.Model/Logging/ILogger.cs b/MediaBrowser.Model/Logging/ILogger.cs
deleted file mode 100644
index be9d6fc50..000000000
--- a/MediaBrowser.Model/Logging/ILogger.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System;
-using System.Text;
-
-namespace MediaBrowser.Model.Logging
-{
- /// <summary>
- /// Interface ILogger
- /// </summary>
- public interface ILogger
- {
- /// <summary>
- /// Infoes the specified message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The param list.</param>
- void Info(string message, params object[] paramList);
-
- /// <summary>
- /// Errors the specified message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The param list.</param>
- void Error(string message, params object[] paramList);
-
- /// <summary>
- /// Warns the specified message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The param list.</param>
- void Warn(string message, params object[] paramList);
-
- /// <summary>
- /// Debugs the specified message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The param list.</param>
- void Debug(string message, params object[] paramList);
-
- /// <summary>
- /// Fatals the specified message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The param list.</param>
- void Fatal(string message, params object[] paramList);
-
- /// <summary>
- /// Fatals the exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- /// <param name="paramList">The param list.</param>
- void FatalException(string message, Exception exception, params object[] paramList);
-
- /// <summary>
- /// Logs the exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- /// <param name="paramList">The param list.</param>
- void ErrorException(string message, Exception exception, params object[] paramList);
-
- /// <summary>
- /// Logs the multiline.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="severity">The severity.</param>
- /// <param name="additionalContent">Content of the additional.</param>
- void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent);
-
- /// <summary>
- /// Logs the specified severity.
- /// </summary>
- /// <param name="severity">The severity.</param>
- /// <param name="message">The message.</param>
- /// <param name="paramList">The parameter list.</param>
- void Log(LogSeverity severity, string message, params object[] paramList);
- }
-}
diff --git a/MediaBrowser.Model/Logging/LogHelper.cs b/MediaBrowser.Model/Logging/LogHelper.cs
deleted file mode 100644
index cf1c02186..000000000
--- a/MediaBrowser.Model/Logging/LogHelper.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Text;
-
-namespace MediaBrowser.Model.Logging
-{
- /// <summary>
- /// Class LogHelper
- /// </summary>
- public static class LogHelper
- {
- /// <summary>
- /// Gets the log message.
- /// </summary>
- /// <param name="exception">The exception.</param>
- /// <returns>StringBuilder.</returns>
- public static StringBuilder GetLogMessage(Exception exception)
- {
- if (exception == null)
- {
- throw new ArgumentNullException("exception");
- }
-
- var messageText = new StringBuilder();
-
- messageText.AppendLine(exception.ToString());
-
- messageText.AppendLine(exception.GetType().FullName);
-
- LogExceptionData(messageText, exception);
-
- messageText.AppendLine(exception.StackTrace ?? "No Stack Trace Available");
-
- // Log the InnerExceptions, if any
- AppendInnerExceptions(messageText, exception);
-
- messageText.AppendLine(string.Empty);
-
- return messageText;
- }
-
- /// <summary>
- /// Appends the inner exceptions.
- /// </summary>
- /// <param name="messageText">The message text.</param>
- /// <param name="e">The e.</param>
- private static void AppendInnerExceptions(StringBuilder messageText, Exception e)
- {
- var aggregate = e as AggregateException;
-
- if (aggregate != null && aggregate.InnerExceptions != null)
- {
- foreach (var ex in aggregate.InnerExceptions)
- {
- AppendInnerException(messageText, ex);
- AppendInnerExceptions(messageText, ex);
- }
- }
-
- else if (e.InnerException != null)
- {
- AppendInnerException(messageText, e.InnerException);
- AppendInnerExceptions(messageText, e.InnerException);
- }
- }
-
- /// <summary>
- /// Appends the inner exception.
- /// </summary>
- /// <param name="messageText">The message text.</param>
- /// <param name="e">The e.</param>
- private static void AppendInnerException(StringBuilder messageText, Exception e)
- {
- messageText.AppendLine("InnerException: " + e.GetType().FullName);
- messageText.AppendLine(e.ToString());
-
- LogExceptionData(messageText, e);
-
- if (e.StackTrace != null)
- {
- messageText.AppendLine(e.StackTrace);
- }
- }
-
- /// <summary>
- /// Logs the exception data.
- /// </summary>
- /// <param name="messageText">The message text.</param>
- /// <param name="e">The e.</param>
- private static void LogExceptionData(StringBuilder messageText, Exception e)
- {
- foreach (var key in e.Data.Keys)
- {
- messageText.AppendLine(key + ": " + e.Data[key]);
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Logging/LogSeverity.cs b/MediaBrowser.Model/Logging/LogSeverity.cs
deleted file mode 100644
index ae0487289..000000000
--- a/MediaBrowser.Model/Logging/LogSeverity.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Logging
-{
- /// <summary>
- /// Enum LogSeverity
- /// </summary>
- public enum LogSeverity
- {
- /// <summary>
- /// The info
- /// </summary>
- Info,
- /// <summary>
- /// The debug
- /// </summary>
- Debug,
- /// <summary>
- /// The warn
- /// </summary>
- Warn,
- /// <summary>
- /// The error
- /// </summary>
- Error,
- /// <summary>
- /// The fatal
- /// </summary>
- Fatal
- }
-}
diff --git a/MediaBrowser.Model/Logging/NullLogger.cs b/MediaBrowser.Model/Logging/NullLogger.cs
deleted file mode 100644
index d211d2567..000000000
--- a/MediaBrowser.Model/Logging/NullLogger.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Text;
-
-namespace MediaBrowser.Model.Logging
-{
- public class NullLogger : ILogger
- {
- public void Info(string message, params object[] paramList)
- {
- }
-
- public void Error(string message, params object[] paramList)
- {
- }
-
- public void Warn(string message, params object[] paramList)
- {
- }
-
- public void Debug(string message, params object[] paramList)
- {
- }
-
- public void Fatal(string message, params object[] paramList)
- {
- }
-
- public void FatalException(string message, Exception exception, params object[] paramList)
- {
- }
-
- public void Log(LogSeverity severity, string message, params object[] paramList)
- {
- }
-
- public void ErrorException(string message, Exception exception, params object[] paramList)
- {
- }
-
- public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent)
- {
- }
- }
-}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
deleted file mode 100644
index dd9b7ff75..000000000
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ /dev/null
@@ -1,449 +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>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Model</RootNamespace>
- <AssemblyName>MediaBrowser.Model</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <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>pdbonly</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="Activity\ActivityLogEntry.cs" />
- <Compile Include="Activity\IActivityManager.cs" />
- <Compile Include="Activity\IActivityRepository.cs" />
- <Compile Include="ApiClient\ConnectSignupResponse.cs" />
- <Compile Include="ApiClient\HttpResponseEventArgs.cs" />
- <Compile Include="ApiClient\GeneralCommandEventArgs.cs" />
- <Compile Include="ApiClient\ServerDiscoveryInfo.cs" />
- <Compile Include="ApiClient\SessionUpdatesEventArgs.cs" />
- <Compile Include="ApiClient\WakeOnLanInfo.cs" />
- <Compile Include="Branding\BrandingOptions.cs" />
- <Compile Include="Channels\AllChannelMediaQuery.cs" />
- <Compile Include="Channels\ChannelFeatures.cs" />
- <Compile Include="Channels\ChannelFolderType.cs" />
- <Compile Include="Channels\ChannelInfo.cs" />
- <Compile Include="Channels\ChannelItemQuery.cs" />
- <Compile Include="Channels\ChannelItemSortField.cs" />
- <Compile Include="Channels\ChannelMediaContentType.cs" />
- <Compile Include="Channels\ChannelMediaType.cs" />
- <Compile Include="Channels\ChannelQuery.cs" />
- <Compile Include="Collections\CollectionCreationResult.cs" />
- <Compile Include="Configuration\AccessSchedule.cs" />
- <Compile Include="Configuration\ChannelOptions.cs" />
- <Compile Include="Configuration\CinemaModeConfiguration.cs" />
- <Compile Include="Configuration\EncodingOptions.cs" />
- <Compile Include="Configuration\FanartOptions.cs" />
- <Compile Include="Configuration\LibraryOptions.cs" />
- <Compile Include="Configuration\MetadataConfiguration.cs" />
- <Compile Include="Configuration\XbmcMetadataOptions.cs" />
- <Compile Include="Configuration\SubtitlePlaybackMode.cs" />
- <Compile Include="Connect\ConnectAuthenticationExchangeResult.cs" />
- <Compile Include="Connect\ConnectAuthenticationResult.cs" />
- <Compile Include="Connect\ConnectAuthorization.cs" />
- <Compile Include="Connect\ConnectAuthorizationRequest.cs" />
- <Compile Include="Connect\ConnectPassword.cs" />
- <Compile Include="Connect\ConnectUser.cs" />
- <Compile Include="Connect\ConnectUserQuery.cs" />
- <Compile Include="Connect\ConnectUserServer.cs" />
- <Compile Include="Connect\PinCreationResult.cs" />
- <Compile Include="Connect\PinExchangeResult.cs" />
- <Compile Include="Connect\PinStatusResult.cs" />
- <Compile Include="Connect\UserLinkType.cs" />
- <Compile Include="Cryptography\ICryptoProvider.cs" />
- <Compile Include="Devices\DeviceOptions.cs" />
- <Compile Include="Devices\DeviceQuery.cs" />
- <Compile Include="Devices\LocalFileInfo.cs" />
- <Compile Include="Devices\DeviceInfo.cs" />
- <Compile Include="Devices\DevicesOptions.cs" />
- <Compile Include="Diagnostics\IProcess.cs" />
- <Compile Include="Diagnostics\IProcessFactory.cs" />
- <Compile Include="Dlna\CodecProfile.cs" />
- <Compile Include="Dlna\ContainerProfile.cs" />
- <Compile Include="Dlna\DeviceProfile.cs" />
- <Compile Include="Dlna\DirectPlayProfile.cs" />
- <Compile Include="Dlna\EncodingContext.cs" />
- <Compile Include="Dlna\HttpHeaderInfo.cs" />
- <Compile Include="Dlna\IDeviceDiscovery.cs" />
- <Compile Include="Dlna\ITranscoderSupport.cs" />
- <Compile Include="Dlna\ProfileCondition.cs" />
- <Compile Include="Dlna\ResponseProfile.cs" />
- <Compile Include="Dlna\StreamInfoSorter.cs" />
- <Compile Include="Dlna\PlaybackErrorCode.cs" />
- <Compile Include="Dlna\ResolutionConfiguration.cs" />
- <Compile Include="Dlna\ResolutionNormalizer.cs" />
- <Compile Include="Dlna\ResolutionOptions.cs" />
- <Compile Include="Dlna\SubtitleDeliveryMethod.cs" />
- <Compile Include="Dlna\SubtitleProfile.cs" />
- <Compile Include="Dlna\SubtitleStreamInfo.cs" />
- <Compile Include="Dlna\TranscodingProfile.cs" />
- <Compile Include="Dlna\UpnpDeviceInfo.cs" />
- <Compile Include="Dlna\XmlAttribute.cs" />
- <Compile Include="Drawing\ImageOrientation.cs" />
- <Compile Include="Dto\IHasServerId.cs" />
- <Compile Include="Dto\IHasSyncInfo.cs" />
- <Compile Include="Dto\MetadataEditorInfo.cs" />
- <Compile Include="Dto\NameIdPair.cs" />
- <Compile Include="Dto\NameValuePair.cs" />
- <Compile Include="Logging\IConsoleLogger.cs" />
- <Compile Include="Net\IpEndPointInfo.cs" />
- <Compile Include="Net\IAcceptSocket.cs" />
- <Compile Include="Net\ISocketFactory.cs" />
- <Compile Include="Net\ISocket.cs" />
- <Compile Include="Net\SocketReceiveResult.cs" />
- <Compile Include="Services\IHttpResult.cs" />
- <Compile Include="Social\ISharingRepository.cs" />
- <Compile Include="System\IEnvironmentInfo.cs" />
- <Compile Include="System\IPowerManagement.cs" />
- <Compile Include="Text\ITextEncoding.cs" />
- <Compile Include="Extensions\LinqExtensions.cs" />
- <Compile Include="IO\FileSystemMetadata.cs" />
- <Compile Include="IO\IFileSystem.cs" />
- <Compile Include="IO\IMemoryStreamFactory.cs" />
- <Compile Include="IO\IShortcutHandler.cs" />
- <Compile Include="IO\StreamDefaults.cs" />
- <Compile Include="Globalization\ILocalizationManager.cs" />
- <Compile Include="Logging\LogHelper.cs" />
- <Compile Include="MediaInfo\LiveStreamRequest.cs" />
- <Compile Include="MediaInfo\LiveStreamResponse.cs" />
- <Compile Include="MediaInfo\PlaybackInfoRequest.cs" />
- <Compile Include="MediaInfo\PlaybackInfoResponse.cs" />
- <Compile Include="Dto\MediaSourceType.cs" />
- <Compile Include="Configuration\DynamicDayOfWeek.cs" />
- <Compile Include="Entities\ExtraType.cs" />
- <Compile Include="Entities\TrailerType.cs" />
- <Compile Include="Configuration\BaseApplicationConfiguration.cs" />
- <Compile Include="Configuration\DlnaOptions.cs" />
- <Compile Include="Configuration\ImageOption.cs" />
- <Compile Include="Configuration\ImageSavingConvention.cs" />
- <Compile Include="LiveTv\LiveTvOptions.cs" />
- <Compile Include="Configuration\MetadataPlugin.cs" />
- <Compile Include="Configuration\MetadataOptions.cs" />
- <Compile Include="Configuration\MetadataPluginSummary.cs" />
- <Compile Include="Configuration\MetadataPluginType.cs" />
- <Compile Include="MediaInfo\MediaProtocol.cs" />
- <Compile Include="MediaInfo\SubtitleTrackEvent.cs" />
- <Compile Include="MediaInfo\SubtitleTrackInfo.cs" />
- <Compile Include="Net\EndPointInfo.cs" />
- <Compile Include="Net\HttpResponse.cs" />
- <Compile Include="Net\IpAddressInfo.cs" />
- <Compile Include="Plugins\IHasWebPages.cs" />
- <Compile Include="Plugins\PluginPageInfo.cs" />
- <Compile Include="Reflection\IAssemblyInfo.cs" />
- <Compile Include="Services\ApiMemberAttribute.cs" />
- <Compile Include="Services\IAsyncStreamWriter.cs" />
- <Compile Include="Services\IHasHeaders.cs" />
- <Compile Include="Services\IHasRequestFilter.cs" />
- <Compile Include="Services\IHttpRequest.cs" />
- <Compile Include="Services\IHttpResponse.cs" />
- <Compile Include="Services\IRequest.cs" />
- <Compile Include="Services\IRequestFilter.cs" />
- <Compile Include="Services\IRequiresRequestStream.cs" />
- <Compile Include="Services\IService.cs" />
- <Compile Include="Net\MimeTypes.cs" />
- <Compile Include="News\INewsService.cs" />
- <Compile Include="Notifications\NotificationOption.cs" />
- <Compile Include="Notifications\NotificationOptions.cs" />
- <Compile Include="Notifications\NotificationType.cs" />
- <Compile Include="Notifications\SendToUserType.cs" />
- <Compile Include="Configuration\ServerConfiguration.cs" />
- <Compile Include="Playlists\PlaylistCreationRequest.cs" />
- <Compile Include="Playlists\PlaylistItemQuery.cs" />
- <Compile Include="Providers\SubtitleOptions.cs" />
- <Compile Include="Configuration\UnratedItem.cs" />
- <Compile Include="Dlna\AudioOptions.cs" />
- <Compile Include="Dlna\CodecType.cs" />
- <Compile Include="Dlna\ConditionProcessor.cs" />
- <Compile Include="Dlna\ContentFeatureBuilder.cs" />
- <Compile Include="Dlna\DeviceIdentification.cs" />
- <Compile Include="Dlna\DeviceProfileInfo.cs" />
- <Compile Include="Dlna\DeviceProfileType.cs" />
- <Compile Include="Dlna\DlnaFlags.cs" />
- <Compile Include="Dlna\DlnaMaps.cs" />
- <Compile Include="Dlna\DlnaProfileType.cs" />
- <Compile Include="Dlna\HeaderMatchType.cs" />
- <Compile Include="Dlna\MediaFormatProfile.cs" />
- <Compile Include="Dlna\MediaFormatProfileResolver.cs" />
- <Compile Include="Dlna\ProfileConditionType.cs" />
- <Compile Include="Dlna\ProfileConditionValue.cs" />
- <Compile Include="Dlna\SearchCriteria.cs" />
- <Compile Include="Dlna\SearchType.cs" />
- <Compile Include="Dlna\SortCriteria.cs" />
- <Compile Include="Dlna\StreamBuilder.cs" />
- <Compile Include="Dlna\StreamInfo.cs" />
- <Compile Include="Dlna\TranscodeSeekInfo.cs" />
- <Compile Include="Dlna\VideoOptions.cs" />
- <Compile Include="Drawing\ImageFormat.cs" />
- <Compile Include="Drawing\ImageSize.cs" />
- <Compile Include="Dto\BaseItemPerson.cs" />
- <Compile Include="Dto\ChapterInfoDto.cs" />
- <Compile Include="Dto\GameSystemSummary.cs" />
- <Compile Include="Dto\IItemDto.cs" />
- <Compile Include="Dto\ImageByNameInfo.cs" />
- <Compile Include="Dto\ImageInfo.cs" />
- <Compile Include="Dto\ItemCounts.cs" />
- <Compile Include="Dto\ItemIndex.cs" />
- <Compile Include="Dto\RatingType.cs" />
- <Compile Include="Dto\RecommendationDto.cs" />
- <Compile Include="Dto\MediaSourceInfo.cs" />
- <Compile Include="Dto\RecommendationType.cs" />
- <Compile Include="Dto\SubtitleDownloadOptions.cs" />
- <Compile Include="Entities\IsoType.cs" />
- <Compile Include="MediaInfo\MediaInfo.cs" />
- <Compile Include="Entities\MediaStreamType.cs" />
- <Compile Include="Entities\PackageReviewInfo.cs" />
- <Compile Include="Entities\ProviderIdsExtensions.cs" />
- <Compile Include="Entities\ScrollDirection.cs" />
- <Compile Include="Entities\SortOrder.cs" />
- <Compile Include="Events\GenericEventArgs.cs" />
- <Compile Include="Extensions\ListHelper.cs" />
- <Compile Include="Extensions\StringHelper.cs" />
- <Compile Include="Globalization\LocalizatonOption.cs" />
- <Compile Include="IO\FileSystemEntryType.cs" />
- <Compile Include="Library\PlayAccess.cs" />
- <Compile Include="Library\UserViewQuery.cs" />
- <Compile Include="LiveTv\BaseTimerInfoDto.cs" />
- <Compile Include="LiveTv\ChannelInfoDto.cs" />
- <Compile Include="LiveTv\LiveTvChannelQuery.cs" />
- <Compile Include="LiveTv\DayPattern.cs" />
- <Compile Include="LiveTv\GuideInfo.cs" />
- <Compile Include="LiveTv\LiveTvInfo.cs" />
- <Compile Include="LiveTv\LiveTvServiceStatus.cs" />
- <Compile Include="LiveTv\LiveTvTunerInfoDto.cs" />
- <Compile Include="LiveTv\LiveTvTunerStatus.cs" />
- <Compile Include="LiveTv\ProgramAudio.cs" />
- <Compile Include="LiveTv\ProgramQuery.cs" />
- <Compile Include="LiveTv\RecommendedProgramQuery.cs" />
- <Compile Include="LiveTv\RecordingGroupQuery.cs" />
- <Compile Include="LiveTv\RecordingQuery.cs" />
- <Compile Include="LiveTv\RecordingStatus.cs" />
- <Compile Include="LiveTv\SeriesTimerInfoDto.cs" />
- <Compile Include="LiveTv\SeriesTimerQuery.cs" />
- <Compile Include="LiveTv\TimerInfoDto.cs" />
- <Compile Include="LiveTv\TimerQuery.cs" />
- <Compile Include="Logging\NullLogger.cs" />
- <Compile Include="MediaInfo\AudioCodec.cs" />
- <Compile Include="MediaInfo\Container.cs" />
- <Compile Include="MediaInfo\SubtitleFormat.cs" />
- <Compile Include="MediaInfo\TransportStreamTimestamp.cs" />
- <Compile Include="MediaInfo\VideoCodec.cs" />
- <Compile Include="News\NewsItem.cs" />
- <Compile Include="News\NewsQuery.cs" />
- <Compile Include="Notifications\NotificationRequest.cs" />
- <Compile Include="Notifications\NotificationServiceInfo.cs" />
- <Compile Include="Notifications\NotificationTypeInfo.cs" />
- <Compile Include="Playlists\PlaylistCreationResult.cs" />
- <Compile Include="Providers\ExternalIdInfo.cs" />
- <Compile Include="Providers\ExternalUrl.cs" />
- <Compile Include="Providers\ImageProviderInfo.cs" />
- <Compile Include="Providers\RemoteImageInfo.cs" />
- <Compile Include="Entities\CollectionType.cs" />
- <Compile Include="Entities\ItemReview.cs" />
- <Compile Include="Entities\MediaUrl.cs" />
- <Compile Include="Entities\MetadataFields.cs" />
- <Compile Include="Entities\UserDataSaveReason.cs" />
- <Compile Include="Entities\Video3DFormat.cs" />
- <Compile Include="IO\IIsoManager.cs" />
- <Compile Include="IO\IIsoMount.cs" />
- <Compile Include="IO\IIsoMounter.cs" />
- <Compile Include="LiveTv\ChannelType.cs" />
- <Compile Include="LiveTv\LiveTvServiceInfo.cs" />
- <Compile Include="Net\WebSocketMessage.cs" />
- <Compile Include="Net\WebSocketMessageType.cs" />
- <Compile Include="Net\WebSocketState.cs" />
- <Compile Include="Notifications\Notification.cs" />
- <Compile Include="Notifications\NotificationLevel.cs" />
- <Compile Include="Notifications\NotificationQuery.cs" />
- <Compile Include="Notifications\NotificationResult.cs" />
- <Compile Include="Notifications\NotificationsSummary.cs" />
- <Compile Include="Providers\RemoteImageQuery.cs" />
- <Compile Include="Providers\RemoteImageResult.cs" />
- <Compile Include="Providers\RemoteSearchResult.cs" />
- <Compile Include="Providers\RemoteSubtitleInfo.cs" />
- <Compile Include="Providers\SubtitleProviderInfo.cs" />
- <Compile Include="Querying\AllThemeMediaResult.cs" />
- <Compile Include="Querying\ArtistsQuery.cs" />
- <Compile Include="Querying\EpisodeQuery.cs" />
- <Compile Include="Querying\ItemCountsQuery.cs" />
- <Compile Include="Querying\ItemsByNameQuery.cs" />
- <Compile Include="Querying\LatestItemsQuery.cs" />
- <Compile Include="Querying\MovieRecommendationQuery.cs" />
- <Compile Include="Querying\NextUpQuery.cs" />
- <Compile Include="Querying\QueryFilters.cs" />
- <Compile Include="Querying\QueryResult.cs" />
- <Compile Include="Querying\SessionQuery.cs" />
- <Compile Include="Querying\SimilarItemsQuery.cs" />
- <Compile Include="Querying\UpcomingEpisodesQuery.cs" />
- <Compile Include="Querying\UserQuery.cs" />
- <Compile Include="Registration\RegistrationInfo.cs" />
- <Compile Include="Search\SearchQuery.cs" />
- <Compile Include="Serialization\IgnoreDataMemberAttribute.cs" />
- <Compile Include="Services\IStreamWriter.cs" />
- <Compile Include="Services\QueryParamCollection.cs" />
- <Compile Include="Services\RouteAttribute.cs" />
- <Compile Include="Session\BrowseRequest.cs" />
- <Compile Include="Session\ClientCapabilities.cs" />
- <Compile Include="Session\GeneralCommand.cs" />
- <Compile Include="Session\GeneralCommandType.cs" />
- <Compile Include="Session\MessageCommand.cs" />
- <Compile Include="Session\PlaybackProgressInfo.cs" />
- <Compile Include="Session\PlaybackStartInfo.cs" />
- <Compile Include="Session\PlaybackStopInfo.cs" />
- <Compile Include="Session\PlayCommand.cs" />
- <Compile Include="Session\PlayerStateInfo.cs" />
- <Compile Include="Session\PlayMethod.cs" />
- <Compile Include="Session\PlayRequest.cs" />
- <Compile Include="Session\PlaystateCommand.cs" />
- <Compile Include="Logging\ILogManager.cs" />
- <Compile Include="MediaInfo\BlurayDiscInfo.cs" />
- <Compile Include="Entities\ChapterInfo.cs" />
- <Compile Include="Entities\LocationType.cs" />
- <Compile Include="Entities\MBRegistrationRecord.cs" />
- <Compile Include="Entities\MediaType.cs" />
- <Compile Include="Entities\PersonType.cs" />
- <Compile Include="Entities\PluginSecurityInfo.cs" />
- <Compile Include="Globalization\CountryInfo.cs" />
- <Compile Include="Globalization\CultureDto.cs" />
- <Compile Include="IO\FileSystemEntryInfo.cs" />
- <Compile Include="Dto\ImageOptions.cs" />
- <Compile Include="Querying\ItemFilter.cs" />
- <Compile Include="Entities\LibraryUpdateInfo.cs" />
- <Compile Include="Entities\ParentalRating.cs" />
- <Compile Include="Entities\VirtualFolderInfo.cs" />
- <Compile Include="IO\IZipClient.cs" />
- <Compile Include="Logging\ILogger.cs" />
- <Compile Include="Logging\LogSeverity.cs" />
- <Compile Include="MediaInfo\IBlurayExaminer.cs" />
- <Compile Include="Net\HttpException.cs" />
- <Compile Include="Net\NetworkShare.cs" />
- <Compile Include="Net\NetworkShareType.cs" />
- <Compile Include="Querying\PersonsQuery.cs" />
- <Compile Include="Querying\ThemeMediaResult.cs" />
- <Compile Include="Search\SearchHint.cs" />
- <Compile Include="Search\SearchHintResult.cs" />
- <Compile Include="Serialization\IJsonSerializer.cs" />
- <Compile Include="Serialization\IXmlSerializer.cs" />
- <Compile Include="Session\PlaystateRequest.cs" />
- <Compile Include="Session\SessionInfoDto.cs" />
- <Compile Include="Session\SessionUserInfo.cs" />
- <Compile Include="Session\TranscodingInfo.cs" />
- <Compile Include="Session\UserDataChangeInfo.cs" />
- <Compile Include="Devices\ContentUploadHistory.cs" />
- <Compile Include="Social\ISharingManager.cs" />
- <Compile Include="Social\SocialShareInfo.cs" />
- <Compile Include="Sync\CompleteSyncJobInfo.cs" />
- <Compile Include="Sync\DeviceFileInfo.cs" />
- <Compile Include="Sync\ItemFIleInfo.cs" />
- <Compile Include="Sync\ItemFileType.cs" />
- <Compile Include="Sync\LocalItem.cs" />
- <Compile Include="Sync\LocalItemInfo.cs" />
- <Compile Include="Sync\LocalItemQuery.cs" />
- <Compile Include="Sync\SyncCategory.cs" />
- <Compile Include="Sync\SyncDataRequest.cs" />
- <Compile Include="Sync\SyncDataResponse.cs" />
- <Compile Include="Sync\SyncDialogOptions.cs" />
- <Compile Include="Sync\SyncedItem.cs" />
- <Compile Include="Sync\SyncJob.cs" />
- <Compile Include="Sync\SyncJobCreationResult.cs" />
- <Compile Include="Sync\SyncJobItem.cs" />
- <Compile Include="Sync\SyncJobItemQuery.cs" />
- <Compile Include="Sync\SyncJobItemStatus.cs" />
- <Compile Include="Sync\SyncJobQuery.cs" />
- <Compile Include="Sync\SyncJobRequest.cs" />
- <Compile Include="Sync\SyncJobStatus.cs" />
- <Compile Include="Sync\SyncOptions.cs" />
- <Compile Include="Sync\SyncParameter.cs" />
- <Compile Include="Sync\SyncProfileOption.cs" />
- <Compile Include="Sync\SyncQualityOption.cs" />
- <Compile Include="Sync\SyncTarget.cs" />
- <Compile Include="System\Architecture.cs" />
- <Compile Include="System\ISystemEvents.cs" />
- <Compile Include="System\LogFile.cs" />
- <Compile Include="System\PublicSystemInfo.cs" />
- <Compile Include="Tasks\IConfigurableScheduledTask.cs" />
- <Compile Include="Tasks\IScheduledTask.cs" />
- <Compile Include="Tasks\IScheduledTaskWorker.cs" />
- <Compile Include="Tasks\ITaskManager.cs" />
- <Compile Include="Tasks\ITaskTrigger.cs" />
- <Compile Include="Tasks\ScheduledTaskHelpers.cs" />
- <Compile Include="Tasks\TaskCompletionEventArgs.cs" />
- <Compile Include="Tasks\TaskExecutionOptions.cs" />
- <Compile Include="Threading\ITimer.cs" />
- <Compile Include="Threading\ITimerFactory.cs" />
- <Compile Include="Updates\CheckForUpdateResult.cs" />
- <Compile Include="Updates\PackageTargetSystem.cs" />
- <Compile Include="Updates\InstallationInfo.cs" />
- <Compile Include="Updates\PackageVersionClass.cs" />
- <Compile Include="Entities\EmptyRequestResult.cs" />
- <Compile Include="Configuration\UserConfiguration.cs" />
- <Compile Include="Drawing\DrawingUtils.cs" />
- <Compile Include="Dto\UserItemDataDto.cs" />
- <Compile Include="Querying\ItemFields.cs" />
- <Compile Include="Querying\ItemSortBy.cs" />
- <Compile Include="Dto\BaseItemDto.cs" />
- <Compile Include="Dto\UserDto.cs" />
- <Compile Include="Entities\DisplayPreferences.cs" />
- <Compile Include="Entities\ImageType.cs" />
- <Compile Include="Entities\IHasProviderIds.cs" />
- <Compile Include="Entities\MetadataProviders.cs" />
- <Compile Include="Entities\SeriesStatus.cs" />
- <Compile Include="Entities\MediaStream.cs" />
- <Compile Include="Entities\VideoType.cs" />
- <Compile Include="Plugins\BasePluginConfiguration.cs" />
- <Compile Include="Plugins\PluginInfo.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="System\SystemInfo.cs" />
- <Compile Include="Tasks\SystemEvent.cs" />
- <Compile Include="Tasks\TaskCompletionStatus.cs" />
- <Compile Include="Tasks\TaskResult.cs" />
- <Compile Include="Tasks\TaskInfo.cs" />
- <Compile Include="Tasks\TaskState.cs" />
- <Compile Include="Tasks\TaskTriggerInfo.cs" />
- <Compile Include="Updates\PackageInfo.cs" />
- <Compile Include="Updates\PackageVersionInfo.cs" />
- <Compile Include="Users\AuthenticationResult.cs" />
- <Compile Include="Users\ForgotPasswordAction.cs" />
- <Compile Include="Users\ForgotPasswordResult.cs" />
- <Compile Include="Users\PinRedeemResult.cs" />
- <Compile Include="Users\UserAction.cs" />
- <Compile Include="Users\UserActionType.cs" />
- <Compile Include="Users\UserPolicy.cs" />
- <Compile Include="Xml\IXmlReaderSettingsFactory.cs" />
- </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.Model/MediaBrowser.Model.nuget.targets b/MediaBrowser.Model/MediaBrowser.Model.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.Model/MediaBrowser.Model.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.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
deleted file mode 100644
index 93aba2f43..000000000
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public class AudioCodec
- {
- public const string AAC = "aac";
- public const string MP3 = "mp3";
- public const string AC3 = "ac3";
-
- public static string GetFriendlyName(string codec)
- {
- if (string.IsNullOrEmpty(codec)) return "";
-
- switch (codec.ToLower())
- {
- case "ac3":
- return "Dolby Digital";
- case "eac3":
- return "Dolby Digital+";
- case "dca":
- return "DTS";
- default:
- return codec.ToUpper();
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
deleted file mode 100644
index 1b573fba7..000000000
--- a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- /// <summary>
- /// Represents the result of BDInfo output
- /// </summary>
- public class BlurayDiscInfo
- {
- /// <summary>
- /// Gets or sets the media streams.
- /// </summary>
- /// <value>The media streams.</value>
- public MediaStream[] MediaStreams { get; set; }
-
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- public long? RunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the files.
- /// </summary>
- /// <value>The files.</value>
- public string[] Files { get; set; }
-
- public string PlaylistName { get; set; }
-
- /// <summary>
- /// Gets or sets the chapters.
- /// </summary>
- /// <value>The chapters.</value>
- public double[] Chapters { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/Container.cs b/MediaBrowser.Model/MediaInfo/Container.cs
deleted file mode 100644
index 3762edf9f..000000000
--- a/MediaBrowser.Model/MediaInfo/Container.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class Container
- {
- public const string MP4 = "mp4";
- public const string MKV = "mkv";
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
deleted file mode 100644
index 78d5b197f..000000000
--- a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Model.MediaInfo
-{
- /// <summary>
- /// Interface IBlurayExaminer
- /// </summary>
- public interface IBlurayExaminer
- {
- /// <summary>
- /// Gets the disc info.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>BlurayDiscInfo.</returns>
- BlurayDiscInfo GetDiscInfo(string path);
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
deleted file mode 100644
index dedbb428d..000000000
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class LiveStreamRequest
- {
- public string OpenToken { get; set; }
- public string UserId { get; set; }
- public string PlaySessionId { get; set; }
- public long? MaxStreamingBitrate { get; set; }
- public long? StartTimeTicks { get; set; }
- public int? AudioStreamIndex { get; set; }
- public int? SubtitleStreamIndex { get; set; }
- public int? MaxAudioChannels { get; set; }
- public string ItemId { get; set; }
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool EnableDirectPlay { get; set; }
- public bool EnableDirectStream { get; set; }
- public bool EnableMediaProbe { get; set; }
-
- public LiveStreamRequest()
- {
- EnableDirectPlay = true;
- EnableDirectStream = true;
- EnableMediaProbe = true;
- }
-
- public LiveStreamRequest(AudioOptions options)
- {
- MaxStreamingBitrate = options.MaxBitrate;
- ItemId = options.ItemId;
- DeviceProfile = options.Profile;
- MaxAudioChannels = options.MaxAudioChannels;
-
- VideoOptions videoOptions = options as VideoOptions;
- if (videoOptions != null)
- {
- AudioStreamIndex = videoOptions.AudioStreamIndex;
- SubtitleStreamIndex = videoOptions.SubtitleStreamIndex;
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
deleted file mode 100644
index e79e37a71..000000000
--- a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class LiveStreamResponse
- {
- public MediaSourceInfo MediaSource { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
deleted file mode 100644
index 63b1c9cfd..000000000
--- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class MediaInfo : MediaSourceInfo, IHasProviderIds
- {
- private static readonly string[] EmptyStringArray = new string[] { };
-
- public ChapterInfo[] Chapters { get; set; }
-
- /// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- public string Album { get; set; }
- /// <summary>
- /// Gets or sets the artists.
- /// </summary>
- /// <value>The artists.</value>
- public string[] Artists { get; set; }
- /// <summary>
- /// Gets or sets the album artists.
- /// </summary>
- /// <value>The album artists.</value>
- public string[] AlbumArtists { get; set; }
- /// <summary>
- /// Gets or sets the studios.
- /// </summary>
- /// <value>The studios.</value>
- public string[] Studios { get; set; }
- public string[] Genres { get; set; }
- public int? IndexNumber { get; set; }
- public int? ParentIndexNumber { get; set; }
- public int? ProductionYear { get; set; }
- public DateTime? PremiereDate { get; set; }
- public BaseItemPerson[] People { get; set; }
- public Dictionary<string, string> ProviderIds { get; set; }
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- public string OfficialRating { get; set; }
- /// <summary>
- /// Gets or sets the official rating description.
- /// </summary>
- /// <value>The official rating description.</value>
- public string OfficialRatingDescription { get; set; }
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
-
- public MediaInfo()
- {
- Chapters = new ChapterInfo[] { };
- Artists = new string[] { };
- AlbumArtists = EmptyStringArray;
- Studios = new string[] { };
- Genres = new string[] { };
- People = new BaseItemPerson[] { };
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs
deleted file mode 100644
index efb31790b..000000000
--- a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public enum MediaProtocol
- {
- File = 0,
- Http = 1,
- Rtmp = 2,
- Rtsp = 3,
- Udp = 4,
- Rtp = 5
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
deleted file mode 100644
index df39f0556..000000000
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class PlaybackInfoRequest
- {
- public string Id { get; set; }
-
- public string UserId { get; set; }
-
- public long? MaxStreamingBitrate { get; set; }
-
- public long? StartTimeTicks { get; set; }
-
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public int? MaxAudioChannels { get; set; }
-
- public string MediaSourceId { get; set; }
-
- public string LiveStreamId { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool EnableDirectPlay { get; set; }
- public bool EnableDirectStream { get; set; }
- public bool EnableTranscoding { get; set; }
- public bool AllowVideoStreamCopy { get; set; }
- public bool AllowAudioStreamCopy { get; set; }
- public bool AutoOpenLiveStream { get; set; }
- public bool EnableMediaProbe { get; set; }
-
- public PlaybackInfoRequest()
- {
- EnableDirectPlay = true;
- EnableDirectStream = true;
- EnableTranscoding = true;
- AllowVideoStreamCopy = true;
- AllowAudioStreamCopy = true;
- EnableMediaProbe = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
deleted file mode 100644
index b38fec7d4..000000000
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class PlaybackInfoResponse
- {
- /// <summary>
- /// Gets or sets the media sources.
- /// </summary>
- /// <value>The media sources.</value>
- public MediaSourceInfo[] MediaSources { get; set; }
-
- /// <summary>
- /// Gets or sets the play session identifier.
- /// </summary>
- /// <value>The play session identifier.</value>
- public string PlaySessionId { get; set; }
-
- /// <summary>
- /// Gets or sets the error code.
- /// </summary>
- /// <value>The error code.</value>
- public PlaybackErrorCode? ErrorCode { get; set; }
-
- public PlaybackInfoResponse()
- {
- MediaSources = new MediaSourceInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
deleted file mode 100644
index 60b0bb54d..000000000
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public class SubtitleFormat
- {
- public const string SRT = "srt";
- public const string SSA = "ssa";
- public const string ASS = "ass";
- public const string VTT = "vtt";
- public const string SUB = "sub";
- public const string SMI = "smi";
- public const string TTML = "ttml";
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
deleted file mode 100644
index b4ab6ed97..000000000
--- a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class SubtitleTrackEvent
- {
- public string Id { get; set; }
- public string Text { get; set; }
- public long StartPositionTicks { get; set; }
- public long EndPositionTicks { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
deleted file mode 100644
index d3a3bb1d0..000000000
--- a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class SubtitleTrackInfo
- {
- public SubtitleTrackEvent[] TrackEvents { get; set; }
-
- public SubtitleTrackInfo()
- {
- TrackEvents = new SubtitleTrackEvent[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
deleted file mode 100644
index 4c808a8dc..000000000
--- a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public enum TransportStreamTimestamp
- {
- None,
- Zero,
- Valid
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaInfo/VideoCodec.cs b/MediaBrowser.Model/MediaInfo/VideoCodec.cs
deleted file mode 100644
index 81755dac9..000000000
--- a/MediaBrowser.Model/MediaInfo/VideoCodec.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public class VideoCodec
- {
- public const string H263 = "h263";
- public const string H264 = "h264";
- public const string H265 = "h265";
- public const string MPEG4 = "mpeg4";
- public const string MPEG1 = "mpeg1video";
- public const string MPEG2 = "mpeg2video";
- public const string MSMPEG4 = "msmpeg4";
- public const string VC1 = "vc1";
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Net/EndPointInfo.cs b/MediaBrowser.Model/Net/EndPointInfo.cs
deleted file mode 100644
index 5a158e785..000000000
--- a/MediaBrowser.Model/Net/EndPointInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Net
-{
- public class EndPointInfo
- {
- public bool IsLocal { get; set; }
- public bool IsInNetwork { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Net/HttpException.cs b/MediaBrowser.Model/Net/HttpException.cs
deleted file mode 100644
index 698b1bc7e..000000000
--- a/MediaBrowser.Model/Net/HttpException.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using System.Net;
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Class HttpException
- /// </summary>
- public class HttpException : Exception
- {
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode? StatusCode { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is timed out.
- /// </summary>
- /// <value><c>true</c> if this instance is timed out; otherwise, <c>false</c>.</value>
- public bool IsTimedOut { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="innerException">The inner exception.</param>
- public HttpException(string message, Exception innerException)
- : base(message, innerException)
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public HttpException(string message)
- : base(message)
- {
- }
- }
-}
diff --git a/MediaBrowser.Model/Net/HttpResponse.cs b/MediaBrowser.Model/Net/HttpResponse.cs
deleted file mode 100644
index 7c3d1d73d..000000000
--- a/MediaBrowser.Model/Net/HttpResponse.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-
-namespace MediaBrowser.Model.Net
-{
- public class HttpResponse : IDisposable
- {
- /// <summary>
- /// Gets or sets the type of the content.
- /// </summary>
- /// <value>The type of the content.</value>
- public string ContentType { get; set; }
-
- /// <summary>
- /// Gets or sets the response URL.
- /// </summary>
- /// <value>The response URL.</value>
- public string ResponseUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- /// <value>The content.</value>
- public Stream Content { get; set; }
-
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode StatusCode { get; set; }
-
- /// <summary>
- /// Gets or sets the length of the content.
- /// </summary>
- /// <value>The length of the content.</value>
- public long? ContentLength { get; set; }
-
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public Dictionary<string, string> Headers { get; set; }
-
- private readonly IDisposable _disposable;
-
- public HttpResponse(IDisposable disposable)
- {
- _disposable = disposable;
- }
- public HttpResponse()
- {
- }
-
- public void Dispose()
- {
- if (_disposable != null)
- {
- _disposable.Dispose();
- }
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs
deleted file mode 100644
index 343e12ab6..000000000
--- a/MediaBrowser.Model/Net/IAcceptSocket.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Net
-{
- public interface IAcceptSocket : IDisposable
- {
- bool DualMode { get; }
- IpEndPointInfo LocalEndPoint { get; }
- IpEndPointInfo RemoteEndPoint { get; }
- void Close();
- void Shutdown(bool both);
- void Listen(int backlog);
- void Bind(IpEndPointInfo endpoint);
- void Connect(IpEndPointInfo endPoint);
- }
-
- public class SocketCreateException : Exception
- {
- public SocketCreateException(string errorCode, Exception originalException)
- : base(errorCode, originalException)
- {
- ErrorCode = errorCode;
- }
-
- public string ErrorCode { get; private set; }
- }
-}
diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs
deleted file mode 100644
index 6a6781026..000000000
--- a/MediaBrowser.Model/Net/ISocket.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Provides a common interface across platforms for UDP sockets used by this SSDP implementation.
- /// </summary>
- public interface ISocket : IDisposable
- {
- IpAddressInfo LocalIPAddress { get; }
-
- Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
-
- int Receive(byte[] buffer, int offset, int count);
-
- IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback);
- SocketReceiveResult EndReceive(IAsyncResult result);
-
- /// <summary>
- /// Sends a UDP message to a particular end point (uni or multicast).
- /// </summary>
- Task SendToAsync(byte[] buffer, int offset, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs
deleted file mode 100644
index bf2424660..000000000
--- a/MediaBrowser.Model/Net/ISocketFactory.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-
-using System.IO;
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="ISocket"/> interface.
- /// </summary>
- public interface ISocketFactory
- {
-
- /// <summary>
- /// Createa a new unicast socket using the specified local port number.
- /// </summary>
- /// <param name="localPort">The local port to bind to.</param>
- /// <returns>A <see cref="ISocket"/> implementation.</returns>
- ISocket CreateUdpSocket(int localPort);
-
- ISocket CreateUdpBroadcastSocket(int localPort);
-
- ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort);
-
- /// <summary>
- /// Createa a new unicast socket using the specified local port number.
- /// </summary>
- ISocket CreateSsdpUdpSocket(IpAddressInfo localIp, int localPort);
-
- /// <summary>
- /// Createa a new multicast socket using the specified multicast IP address, multicast time to live and local port.
- /// </summary>
- /// <param name="ipAddress">The multicast IP address to bind to.</param>
- /// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param>
- /// <param name="localPort">The local port to bind to.</param>
- /// <returns>A <see cref="ISocket"/> implementation.</returns>
- ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort);
-
- IAcceptSocket CreateSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode);
-
- Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
- }
-
- public enum SocketType
- {
- Stream
- }
-
- public enum ProtocolType
- {
- Tcp
- }
-}
diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs
deleted file mode 100644
index 57a0039c4..000000000
--- a/MediaBrowser.Model/Net/IpAddressInfo.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Net
-{
- public class IpAddressInfo
- {
- public static IpAddressInfo Any = new IpAddressInfo("0.0.0.0", IpAddressFamily.InterNetwork);
- public static IpAddressInfo IPv6Any = new IpAddressInfo("00000000000000000000", IpAddressFamily.InterNetworkV6);
- public static IpAddressInfo Loopback = new IpAddressInfo("127.0.0.1", IpAddressFamily.InterNetwork);
- public static IpAddressInfo IPv6Loopback = new IpAddressInfo("::1", IpAddressFamily.InterNetworkV6);
-
- public string Address { get; set; }
- public IpAddressFamily AddressFamily { get; set; }
-
- public IpAddressInfo(string address, IpAddressFamily addressFamily)
- {
- if (string.IsNullOrWhiteSpace(address))
- {
- throw new ArgumentNullException("address");
- }
-
- Address = address;
- AddressFamily = addressFamily;
- }
-
- public bool Equals(IpAddressInfo address)
- {
- return string.Equals(address.Address, Address, StringComparison.OrdinalIgnoreCase);
- }
-
- public override String ToString()
- {
- return Address;
- }
- }
-
- public enum IpAddressFamily
- {
- InterNetwork,
- InterNetworkV6
- }
-}
diff --git a/MediaBrowser.Model/Net/IpEndPointInfo.cs b/MediaBrowser.Model/Net/IpEndPointInfo.cs
deleted file mode 100644
index b5cadc429..000000000
--- a/MediaBrowser.Model/Net/IpEndPointInfo.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace MediaBrowser.Model.Net
-{
- public class IpEndPointInfo
- {
- public IpAddressInfo IpAddress { get; set; }
-
- public int Port { get; set; }
-
- public IpEndPointInfo()
- {
-
- }
-
- public IpEndPointInfo(IpAddressInfo address, int port)
- {
- IpAddress = address;
- Port = port;
- }
-
- public override string ToString()
- {
- var ipAddresString = IpAddress == null ? string.Empty : IpAddress.ToString();
-
- return ipAddresString + ":" + Port.ToString(CultureInfo.InvariantCulture);
- }
- }
-}
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
deleted file mode 100644
index c4dfd25ca..000000000
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ /dev/null
@@ -1,359 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Class MimeTypes
- /// </summary>
- public static class MimeTypes
- {
- /// <summary>
- /// Any extension in this list is considered a video file - can be added to at runtime for extensibility
- /// </summary>
- private static readonly string[] VideoFileExtensions = new string[]
- {
- ".mkv",
- ".m2t",
- ".m2ts",
- ".img",
- ".iso",
- ".mk3d",
- ".ts",
- ".rmvb",
- ".mov",
- ".avi",
- ".mpg",
- ".mpeg",
- ".wmv",
- ".mp4",
- ".divx",
- ".dvr-ms",
- ".wtv",
- ".ogm",
- ".ogv",
- ".asf",
- ".m4v",
- ".flv",
- ".f4v",
- ".3gp",
- ".webm",
- ".mts",
- ".m2v",
- ".rec"
- };
-
- private static Dictionary<string, string> GetVideoFileExtensionsDictionary()
- {
- Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- foreach (string ext in VideoFileExtensions)
- {
- dict[ext] = ext;
- }
-
- return dict;
- }
-
- private static readonly Dictionary<string, string> VideoFileExtensionsDictionary = GetVideoFileExtensionsDictionary();
-
- // http://en.wikipedia.org/wiki/Internet_media_type
- // Add more as needed
-
- private static Dictionary<string, string> GetMimeTypeLookup()
- {
- Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- dict.Add(".jpg", "image/jpeg");
- dict.Add(".jpeg", "image/jpeg");
- dict.Add(".tbn", "image/jpeg");
- dict.Add(".png", "image/png");
- dict.Add(".gif", "image/gif");
- dict.Add(".tiff", "image/tiff");
- dict.Add(".webp", "image/webp");
- dict.Add(".ico", "image/vnd.microsoft.icon");
- dict.Add(".mpg", "video/mpeg");
- dict.Add(".mpeg", "video/mpeg");
- dict.Add(".ogv", "video/ogg");
- dict.Add(".mov", "video/quicktime");
- dict.Add(".webm", "video/webm");
- dict.Add(".mkv", "video/x-matroska");
- dict.Add(".wmv", "video/x-ms-wmv");
- dict.Add(".flv", "video/x-flv");
- dict.Add(".avi", "video/x-msvideo");
- dict.Add(".asf", "video/x-ms-asf");
- dict.Add(".m4v", "video/x-m4v");
-
- return dict;
- }
-
- private static readonly Dictionary<string, string> MimeTypeLookup = GetMimeTypeLookup();
-
- private static readonly Dictionary<string, string> ExtensionLookup = CreateExtensionLookup();
-
- private static Dictionary<string, string> CreateExtensionLookup()
- {
- var dict = MimeTypeLookup
- .GroupBy(i => i.Value)
- .ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase);
-
- dict["image/jpg"] = ".jpg";
- dict["image/x-png"] = ".png";
-
- return dict;
- }
-
- public static string GetMimeType(string path)
- {
- return GetMimeType(path, true);
- }
-
- /// <summary>
- /// Gets the type of the MIME.
- /// </summary>
- public static string GetMimeType(string path, bool enableStreamDefault)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var ext = Path.GetExtension(path) ?? string.Empty;
-
- string result;
- if (MimeTypeLookup.TryGetValue(ext, out result))
- {
- return result;
- }
-
- // Type video
- if (StringHelper.EqualsIgnoreCase(ext, ".3gp"))
- {
- return "video/3gpp";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".3g2"))
- {
- return "video/3gpp2";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ts"))
- {
- return "video/mp2t";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".mpd"))
- {
- return "video/vnd.mpeg.dash.mpd";
- }
-
- // Catch-all for all video types that don't require specific mime types
- if (VideoFileExtensionsDictionary.ContainsKey(ext))
- {
- return "video/" + ext.TrimStart('.').ToLower();
- }
-
- // Type text
- if (StringHelper.EqualsIgnoreCase(ext, ".css"))
- {
- return "text/css";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".csv"))
- {
- return "text/csv";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".html"))
- {
- return "text/html; charset=UTF-8";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".htm"))
- {
- return "text/html; charset=UTF-8";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".txt"))
- {
- return "text/plain";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".xml"))
- {
- return "application/xml";
- }
-
- // Type document
- if (StringHelper.EqualsIgnoreCase(ext, ".pdf"))
- {
- return "application/pdf";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".mobi"))
- {
- return "application/x-mobipocket-ebook";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".epub"))
- {
- return "application/epub+zip";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".cbz"))
- {
- return "application/epub+zip";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".cbr"))
- {
- return "application/epub+zip";
- }
-
- // Type audio
- if (StringHelper.EqualsIgnoreCase(ext, ".mp3"))
- {
- return "audio/mpeg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".m4a"))
- {
- return "audio/mp4";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
- {
- return "audio/mp4";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".webma"))
- {
- return "audio/webm";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".wav"))
- {
- return "audio/wav";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".wma"))
- {
- return "audio/x-ms-wma";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".flac"))
- {
- return "audio/flac";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
- {
- return "audio/x-aac";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ogg"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".oga"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
- {
- return "audio/ogg";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".ac3"))
- {
- return "audio/ac3";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".dsf"))
- {
- return "audio/dsf";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".m4b"))
- {
- return "audio/m4b";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".xsp"))
- {
- return "audio/xsp";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".dsp"))
- {
- return "audio/dsp";
- }
-
- // Playlists
- if (StringHelper.EqualsIgnoreCase(ext, ".m3u8"))
- {
- return "application/x-mpegURL";
- }
-
- // Misc
- if (StringHelper.EqualsIgnoreCase(ext, ".dll"))
- {
- return "application/octet-stream";
- }
-
- // Web
- if (StringHelper.EqualsIgnoreCase(ext, ".js"))
- {
- return "application/x-javascript";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".json"))
- {
- return "application/json";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".map"))
- {
- return "application/x-javascript";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".woff"))
- {
- return "font/woff";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".ttf"))
- {
- return "font/ttf";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".eot"))
- {
- return "application/vnd.ms-fontobject";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".svg"))
- {
- return "image/svg+xml";
- }
- if (StringHelper.EqualsIgnoreCase(ext, ".svgz"))
- {
- return "image/svg+xml";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".srt"))
- {
- return "text/plain";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".vtt"))
- {
- return "text/vtt";
- }
-
- if (StringHelper.EqualsIgnoreCase(ext, ".ttml"))
- {
- return "application/ttml+xml";
- }
-
- if (enableStreamDefault)
- {
- return "application/octet-stream";
- }
-
- return null;
- }
-
- public static string ToExtension(string mimeType)
- {
- if (string.IsNullOrEmpty(mimeType))
- {
- throw new ArgumentNullException("mimeType");
- }
-
- // handle text/html; charset=UTF-8
- mimeType = mimeType.Split(';')[0];
-
- string result;
- if (ExtensionLookup.TryGetValue(mimeType, out result))
- {
- return result;
- }
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Model/Net/NetworkShare.cs b/MediaBrowser.Model/Net/NetworkShare.cs
deleted file mode 100644
index 5ce84eeed..000000000
--- a/MediaBrowser.Model/Net/NetworkShare.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- public class NetworkShare
- {
- /// <summary>
- /// The name of the computer that this share belongs to
- /// </summary>
- public string Server { get; set; }
-
- /// <summary>
- /// Share name
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Local path
- /// </summary>
- public string Path { get; set; }
-
- /// <summary>
- /// Share type
- /// </summary>
- public NetworkShareType ShareType { get; set; }
-
- /// <summary>
- /// Comment
- /// </summary>
- public string Remark { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Net/NetworkShareType.cs b/MediaBrowser.Model/Net/NetworkShareType.cs
deleted file mode 100644
index 41dc9003e..000000000
--- a/MediaBrowser.Model/Net/NetworkShareType.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Enum NetworkShareType
- /// </summary>
- public enum NetworkShareType
- {
- /// <summary>
- /// Disk share
- /// </summary>
- Disk,
- /// <summary>
- /// Printer share
- /// </summary>
- Printer,
- /// <summary>
- /// Device share
- /// </summary>
- Device,
- /// <summary>
- /// IPC share
- /// </summary>
- Ipc,
- /// <summary>
- /// Special share
- /// </summary>
- Special
- }
-}
diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs
deleted file mode 100644
index 483e2297b..000000000
--- a/MediaBrowser.Model/Net/SocketReceiveResult.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Used by the sockets wrapper to hold raw data received from a UDP socket.
- /// </summary>
- public sealed class SocketReceiveResult
- {
- /// <summary>
- /// The buffer to place received data into.
- /// </summary>
- public byte[] Buffer { get; set; }
-
- /// <summary>
- /// The number of bytes received.
- /// </summary>
- public int ReceivedBytes { get; set; }
-
- /// <summary>
- /// The <see cref="IpEndPointInfo"/> the data was received from.
- /// </summary>
- public IpEndPointInfo RemoteEndPoint { get; set; }
- public IpAddressInfo LocalIPAddress { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs
deleted file mode 100644
index 2cd6828b3..000000000
--- a/MediaBrowser.Model/Net/WebSocketMessage.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Class WebSocketMessage
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class WebSocketMessage<T>
- {
- /// <summary>
- /// Gets or sets the type of the message.
- /// </summary>
- /// <value>The type of the message.</value>
- public string MessageType { get; set; }
- /// <summary>
- /// Gets or sets the data.
- /// </summary>
- /// <value>The data.</value>
- public T Data { get; set; }
- }
-
-}
diff --git a/MediaBrowser.Model/Net/WebSocketMessageType.cs b/MediaBrowser.Model/Net/WebSocketMessageType.cs
deleted file mode 100644
index 37ae7fc55..000000000
--- a/MediaBrowser.Model/Net/WebSocketMessageType.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Enum WebSocketMessageType
- /// </summary>
- public enum WebSocketMessageType
- {
- /// <summary>
- /// The text
- /// </summary>
- Text,
- /// <summary>
- /// The binary
- /// </summary>
- Binary,
- /// <summary>
- /// The close
- /// </summary>
- Close
- }
-}
diff --git a/MediaBrowser.Model/Net/WebSocketState.cs b/MediaBrowser.Model/Net/WebSocketState.cs
deleted file mode 100644
index 41a2e9741..000000000
--- a/MediaBrowser.Model/Net/WebSocketState.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Enum WebSocketState
- /// </summary>
- public enum WebSocketState
- {
- /// <summary>
- /// The none
- /// </summary>
- None,
- /// <summary>
- /// The connecting
- /// </summary>
- Connecting,
- /// <summary>
- /// The open
- /// </summary>
- Open,
- /// <summary>
- /// The close sent
- /// </summary>
- CloseSent,
- /// <summary>
- /// The close received
- /// </summary>
- CloseReceived,
- /// <summary>
- /// The closed
- /// </summary>
- Closed,
- /// <summary>
- /// The aborted
- /// </summary>
- Aborted
- }
-}
diff --git a/MediaBrowser.Model/News/INewsService.cs b/MediaBrowser.Model/News/INewsService.cs
deleted file mode 100644
index 4c92664d9..000000000
--- a/MediaBrowser.Model/News/INewsService.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.News
-{
- /// <summary>
- /// Interface INewsFeed
- /// </summary>
- public interface INewsService
- {
- /// <summary>
- /// Gets the product news.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult{NewsItem}.</returns>
- QueryResult<NewsItem> GetProductNews(NewsQuery query);
- }
-}
diff --git a/MediaBrowser.Model/News/NewsItem.cs b/MediaBrowser.Model/News/NewsItem.cs
deleted file mode 100644
index 2a05c420a..000000000
--- a/MediaBrowser.Model/News/NewsItem.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.News
-{
- public class NewsItem
- {
- public string Title { get; set; }
- public string Link { get; set; }
- public string Description { get; set; }
- public string DescriptionHtml { get; set; }
- public string Guid { get; set; }
- public DateTime Date { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/News/NewsQuery.cs b/MediaBrowser.Model/News/NewsQuery.cs
deleted file mode 100644
index 567888921..000000000
--- a/MediaBrowser.Model/News/NewsQuery.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.News
-{
- public class NewsQuery
- {
- public int? StartIndex { get; set; }
-
- public int? Limit { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/Notification.cs b/MediaBrowser.Model/Notifications/Notification.cs
deleted file mode 100644
index 5439b838d..000000000
--- a/MediaBrowser.Model/Notifications/Notification.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class Notification
- {
- public string Id { get; set; }
-
- public string UserId { get; set; }
-
- public DateTime Date { get; set; }
-
- public bool IsRead { get; set; }
-
- public string Name { get; set; }
-
- public string Description { get; set; }
-
- public string Url { get; set; }
-
- public NotificationLevel Level { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationLevel.cs b/MediaBrowser.Model/Notifications/NotificationLevel.cs
deleted file mode 100644
index a49ee2fe6..000000000
--- a/MediaBrowser.Model/Notifications/NotificationLevel.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Notifications
-{
- public enum NotificationLevel
- {
- Normal = 0,
- Warning = 1,
- Error = 2
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs
deleted file mode 100644
index e8a7178c4..000000000
--- a/MediaBrowser.Model/Notifications/NotificationOption.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationOption
- {
- public string Type { get; set; }
-
- /// <summary>
- /// User Ids to not monitor (it's opt out)
- /// </summary>
- public string[] DisabledMonitorUsers { get; set; }
-
- /// <summary>
- /// User Ids to send to (if SendToUserMode == Custom)
- /// </summary>
- public string[] SendToUsers { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="NotificationOption"/> is enabled.
- /// </summary>
- /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
- public bool Enabled { get; set; }
-
- /// <summary>
- /// Gets or sets the title format string.
- /// </summary>
- /// <value>The title format string.</value>
- public string Title { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description { get; set; }
-
- /// <summary>
- /// Gets or sets the disabled services.
- /// </summary>
- /// <value>The disabled services.</value>
- public string[] DisabledServices { get; set; }
-
- /// <summary>
- /// Gets or sets the send to user mode.
- /// </summary>
- /// <value>The send to user mode.</value>
- public SendToUserType SendToUserMode { get; set; }
-
- public NotificationOption()
- {
- DisabledServices = new string[] { };
- DisabledMonitorUsers = new string[] { };
- SendToUsers = new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
deleted file mode 100644
index 7a33c92f9..000000000
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationOptions
- {
- public NotificationOption[] Options { get; set; }
-
- public NotificationOptions()
- {
- Options = new[]
- {
- new NotificationOption
- {
- Type = NotificationType.TaskFailed.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.ServerRestartRequired.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.ApplicationUpdateAvailable.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.ApplicationUpdateInstalled.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.PluginUpdateInstalled.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.PluginUninstalled.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.InstallationFailed.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.PluginInstalled.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.PluginError.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption
- {
- Type = NotificationType.UserLockedOut.ToString(),
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- }
- };
- }
-
- public NotificationOption GetOptions(string type)
- {
- foreach (NotificationOption i in Options)
- {
- if (StringHelper.EqualsIgnoreCase(type, i.Type)) return i;
- }
- return null;
- }
-
- public bool IsEnabled(string type)
- {
- NotificationOption opt = GetOptions(type);
-
- return opt != null && opt.Enabled;
- }
-
- public bool IsServiceEnabled(string service, string notificationType)
- {
- NotificationOption opt = GetOptions(notificationType);
-
- return opt == null ||
- !ListHelper.ContainsIgnoreCase(opt.DisabledServices, service);
- }
-
- public bool IsEnabledToMonitorUser(string type, string userId)
- {
- NotificationOption opt = GetOptions(type);
-
- return opt != null && opt.Enabled &&
- !ListHelper.ContainsIgnoreCase(opt.DisabledMonitorUsers, userId);
- }
-
- public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy)
- {
- NotificationOption opt = GetOptions(type);
-
- if (opt != null && opt.Enabled)
- {
- if (opt.SendToUserMode == SendToUserType.All)
- {
- return true;
- }
-
- if (opt.SendToUserMode == SendToUserType.Admins && userPolicy.IsAdministrator)
- {
- return true;
- }
-
- return ListHelper.ContainsIgnoreCase(opt.SendToUsers, userId);
- }
-
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationQuery.cs b/MediaBrowser.Model/Notifications/NotificationQuery.cs
deleted file mode 100644
index 7e8814d7a..000000000
--- a/MediaBrowser.Model/Notifications/NotificationQuery.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationQuery
- {
- public string UserId { get; set; }
-
- public bool? IsRead { get; set; }
-
- public int? StartIndex { get; set; }
-
- public int? Limit { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs
deleted file mode 100644
index 3cd463422..000000000
--- a/MediaBrowser.Model/Notifications/NotificationRequest.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationRequest
- {
- public string Name { get; set; }
-
- public string Description { get; set; }
-
- public string Url { get; set; }
-
- public NotificationLevel Level { get; set; }
-
- public List<string> UserIds { get; set; }
-
- public DateTime Date { get; set; }
-
- /// <summary>
- /// The corresponding type name used in configuration. Not for display.
- /// </summary>
- public string NotificationType { get; set; }
-
- public Dictionary<string, string> Variables { get; set; }
-
- public SendToUserType? SendToUserMode { get; set; }
-
- public NotificationRequest()
- {
- UserIds = new List<string>();
- Date = DateTime.UtcNow;
-
- Variables = new Dictionary<string, string>();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationResult.cs b/MediaBrowser.Model/Notifications/NotificationResult.cs
deleted file mode 100644
index a98fe4edb..000000000
--- a/MediaBrowser.Model/Notifications/NotificationResult.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationResult
- {
- public Notification[] Notifications { get; set; }
- public int TotalRecordCount { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs b/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs
deleted file mode 100644
index 0ffe7d4ae..000000000
--- a/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationServiceInfo
- {
- public string Name { get; set; }
- public string Id { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs
deleted file mode 100644
index eefd15808..000000000
--- a/MediaBrowser.Model/Notifications/NotificationType.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace MediaBrowser.Model.Notifications
-{
- public enum NotificationType
- {
- ApplicationUpdateAvailable,
- ApplicationUpdateInstalled,
- AudioPlayback,
- GamePlayback,
- VideoPlayback,
- AudioPlaybackStopped,
- GamePlaybackStopped,
- VideoPlaybackStopped,
- InstallationFailed,
- PluginError,
- PluginInstalled,
- PluginUpdateInstalled,
- PluginUninstalled,
- NewLibraryContent,
- ServerRestartRequired,
- TaskFailed,
- CameraImageUploaded,
- UserLockedOut
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
deleted file mode 100644
index ee5101011..000000000
--- a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationTypeInfo
- {
- public string Type { get; set; }
-
- public string Name { get; set; }
-
- public bool Enabled { get; set; }
-
- public string Category { get; set; }
-
- public bool IsBasedOnUserEvent { get; set; }
-
- public string DefaultTitle { get; set; }
-
- public string DefaultDescription { get; set; }
-
- public string[] Variables { get; set; }
-
- public NotificationTypeInfo()
- {
- Variables = new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationsSummary.cs b/MediaBrowser.Model/Notifications/NotificationsSummary.cs
deleted file mode 100644
index 87dd51a5f..000000000
--- a/MediaBrowser.Model/Notifications/NotificationsSummary.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationsSummary
- {
- public int UnreadCount { get; set; }
- public NotificationLevel MaxUnreadNotificationLevel { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/SendToUserType.cs b/MediaBrowser.Model/Notifications/SendToUserType.cs
deleted file mode 100644
index 1998d3102..000000000
--- a/MediaBrowser.Model/Notifications/SendToUserType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Notifications
-{
- public enum SendToUserType
- {
- All = 0,
- Admins = 1,
- Custom = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
deleted file mode 100644
index 5314e791a..000000000
--- a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Playlists
-{
- public class PlaylistCreationRequest
- {
- public string Name { get; set; }
-
- public string[] ItemIdList { get; set; }
-
- public string MediaType { get; set; }
-
- public string UserId { get; set; }
-
- public PlaylistCreationRequest()
- {
- ItemIdList = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
deleted file mode 100644
index bbab8a18d..000000000
--- a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Model.Playlists
-{
- public class PlaylistCreationResult
- {
- public string Id { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
deleted file mode 100644
index 0f6a0c8c5..000000000
--- a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Playlists
-{
- public class PlaylistItemQuery
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// Gets or sets the fields.
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs b/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs
deleted file mode 100644
index 9a8bfadd1..000000000
--- a/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Plugins
-{
- /// <summary>
- /// Class BasePluginConfiguration
- /// </summary>
- public class BasePluginConfiguration
- {
- }
-}
diff --git a/MediaBrowser.Model/Plugins/IHasWebPages.cs b/MediaBrowser.Model/Plugins/IHasWebPages.cs
deleted file mode 100644
index 0745c3c60..000000000
--- a/MediaBrowser.Model/Plugins/IHasWebPages.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Plugins
-{
- public interface IHasWebPages
- {
- IEnumerable<PluginPageInfo> GetPages();
- }
-}
diff --git a/MediaBrowser.Model/Plugins/PluginInfo.cs b/MediaBrowser.Model/Plugins/PluginInfo.cs
deleted file mode 100644
index 06fa2322b..000000000
--- a/MediaBrowser.Model/Plugins/PluginInfo.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Plugins
-{
- /// <summary>
- /// This is a serializable stub class that is used by the api to provide information about installed plugins.
- /// </summary>
- public class PluginInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the configuration date last modified.
- /// </summary>
- /// <value>The configuration date last modified.</value>
- public DateTime ConfigurationDateLastModified { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the assembly file.
- /// </summary>
- /// <value>The name of the assembly file.</value>
- public string AssemblyFileName { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the configuration file.
- /// </summary>
- /// <value>The name of the configuration file.</value>
- public string ConfigurationFileName { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description { get; set; }
-
- /// <summary>
- /// Gets or sets the unique id.
- /// </summary>
- /// <value>The unique id.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets the image URL.
- /// </summary>
- /// <value>The image URL.</value>
- public string ImageUrl { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
deleted file mode 100644
index 64cdb31f8..000000000
--- a/MediaBrowser.Model/Plugins/PluginPageInfo.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.Plugins
-{
- public class PluginPageInfo
- {
- public string Name { get; set; }
-
- public string DisplayName { get; set; }
-
- public string EmbeddedResourcePath { get; set; }
-
- public bool EnableInMainMenu { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Properties/AssemblyInfo.cs b/MediaBrowser.Model/Properties/AssemblyInfo.cs
deleted file mode 100644
index fabfd908b..000000000
--- a/MediaBrowser.Model/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Reflection;
-using System.Resources;
-
-// 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.Model")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Model")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-// \ No newline at end of file
diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs
deleted file mode 100644
index 2c5cfe91b..000000000
--- a/MediaBrowser.Model/Providers/ExternalIdInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-namespace MediaBrowser.Model.Providers
-{
- public class ExternalIdInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
-
- /// <summary>
- /// Gets or sets the URL format string.
- /// </summary>
- /// <value>The URL format string.</value>
- public string UrlFormatString { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Providers/ExternalUrl.cs b/MediaBrowser.Model/Providers/ExternalUrl.cs
deleted file mode 100644
index fb744f446..000000000
--- a/MediaBrowser.Model/Providers/ExternalUrl.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace MediaBrowser.Model.Providers
-{
- public class ExternalUrl
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the item.
- /// </summary>
- /// <value>The type of the item.</value>
- public string Url { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Providers/ImageProviderInfo.cs b/MediaBrowser.Model/Providers/ImageProviderInfo.cs
deleted file mode 100644
index 199552640..000000000
--- a/MediaBrowser.Model/Providers/ImageProviderInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Providers
-{
- /// <summary>
- /// Class ImageProviderInfo.
- /// </summary>
- public class ImageProviderInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- public ImageType[] SupportedImages { get; set; }
-
- public ImageProviderInfo()
- {
- SupportedImages = new ImageType[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs
deleted file mode 100644
index 6db7f77bd..000000000
--- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Providers
-{
- /// <summary>
- /// Class RemoteImageInfo
- /// </summary>
- public class RemoteImageInfo
- {
- /// <summary>
- /// Gets or sets the name of the provider.
- /// </summary>
- /// <value>The name of the provider.</value>
- public string ProviderName { get; set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets a url used for previewing a smaller version
- /// </summary>
- public string ThumbnailUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- public double? CommunityRating { get; set; }
-
- /// <summary>
- /// Gets or sets the vote count.
- /// </summary>
- /// <value>The vote count.</value>
- public int? VoteCount { get; set; }
-
- /// <summary>
- /// Gets or sets the language.
- /// </summary>
- /// <value>The language.</value>
- public string Language { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public ImageType Type { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the rating.
- /// </summary>
- /// <value>The type of the rating.</value>
- public RatingType RatingType { get; set; }
- }
-
-}
diff --git a/MediaBrowser.Model/Providers/RemoteImageQuery.cs b/MediaBrowser.Model/Providers/RemoteImageQuery.cs
deleted file mode 100644
index 8d5231a25..000000000
--- a/MediaBrowser.Model/Providers/RemoteImageQuery.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Providers
-{
- public class RemoteImageQuery
- {
- public string ProviderName { get; set; }
-
- public ImageType? ImageType { get; set; }
-
- public bool IncludeDisabledProviders { get; set; }
-
- public bool IncludeAllLanguages { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Providers/RemoteImageResult.cs b/MediaBrowser.Model/Providers/RemoteImageResult.cs
deleted file mode 100644
index 7e38badfc..000000000
--- a/MediaBrowser.Model/Providers/RemoteImageResult.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Providers
-{
- /// <summary>
- /// Class RemoteImageResult.
- /// </summary>
- public class RemoteImageResult
- {
- /// <summary>
- /// Gets or sets the images.
- /// </summary>
- /// <value>The images.</value>
- public RemoteImageInfo[] Images { get; set; }
-
- /// <summary>
- /// Gets or sets the total record count.
- /// </summary>
- /// <value>The total record count.</value>
- public int TotalRecordCount { get; set; }
-
- /// <summary>
- /// Gets or sets the providers.
- /// </summary>
- /// <value>The providers.</value>
- public string[] Providers { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs
deleted file mode 100644
index fdff7ba46..000000000
--- a/MediaBrowser.Model/Providers/RemoteSearchResult.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Providers
-{
- public class RemoteSearchResult : IHasProviderIds
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the provider ids.
- /// </summary>
- /// <value>The provider ids.</value>
- public Dictionary<string, string> ProviderIds { get; set; }
- /// <summary>
- /// Gets or sets the year.
- /// </summary>
- /// <value>The year.</value>
- public int? ProductionYear { get; set; }
- public int? IndexNumber { get; set; }
- public int? IndexNumberEnd { get; set; }
- public int? ParentIndexNumber { get; set; }
-
- public DateTime? PremiereDate { get; set; }
-
- public string ImageUrl { get; set; }
-
- public string SearchProviderName { get; set; }
-
- public string GameSystem { get; set; }
- public string Overview { get; set; }
-
- public RemoteSearchResult AlbumArtist { get; set; }
-
- public RemoteSearchResult()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
deleted file mode 100644
index 0a4a52cd5..000000000
--- a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Providers
-{
- public class RemoteSubtitleInfo
- {
- public string ThreeLetterISOLanguageName { get; set; }
- public string Id { get; set; }
- public string ProviderName { get; set; }
- public string Name { get; set; }
- public string Format { get; set; }
- public string Author { get; set; }
- public string Comment { get; set; }
- public DateTime? DateCreated { get; set; }
- public float? CommunityRating { get; set; }
- public int? DownloadCount { get; set; }
- public bool? IsHashMatch { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs
deleted file mode 100644
index 5587e897f..000000000
--- a/MediaBrowser.Model/Providers/SubtitleOptions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Model.Providers
-{
- public class SubtitleOptions
- {
- public bool SkipIfEmbeddedSubtitlesPresent { get; set; }
- public bool SkipIfAudioTrackMatches { get; set; }
- public string[] DownloadLanguages { get; set; }
- public bool DownloadMovieSubtitles { get; set; }
- public bool DownloadEpisodeSubtitles { get; set; }
-
- public string OpenSubtitlesUsername { get; set; }
- public string OpenSubtitlesPasswordHash { get; set; }
- public bool IsOpenSubtitleVipAccount { get; set; }
-
- public bool RequirePerfectMatch { get; set; }
-
- public SubtitleOptions()
- {
- DownloadLanguages = new string[] { };
-
- SkipIfAudioTrackMatches = true;
- RequirePerfectMatch = true;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
deleted file mode 100644
index ecce18bd5..000000000
--- a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Providers
-{
- public class SubtitleProviderInfo
- {
- public string Name { get; set; }
- public string Id { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
deleted file mode 100644
index 89640eb65..000000000
--- a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- public class AllThemeMediaResult
- {
- public ThemeMediaResult ThemeVideosResult { get; set; }
-
- public ThemeMediaResult ThemeSongsResult { get; set; }
-
- public ThemeMediaResult SoundtrackSongsResult { get; set; }
-
- public AllThemeMediaResult()
- {
- ThemeVideosResult = new ThemeMediaResult();
-
- ThemeSongsResult = new ThemeMediaResult();
-
- SoundtrackSongsResult = new ThemeMediaResult();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Querying/ArtistsQuery.cs b/MediaBrowser.Model/Querying/ArtistsQuery.cs
deleted file mode 100644
index 471757196..000000000
--- a/MediaBrowser.Model/Querying/ArtistsQuery.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class ArtistsQuery
- /// </summary>
- public class ArtistsQuery : ItemsByNameQuery
- {
- }
-}
diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs
deleted file mode 100644
index 78fe943e3..000000000
--- a/MediaBrowser.Model/Querying/EpisodeQuery.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- public class EpisodeQuery
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the season identifier.
- /// </summary>
- /// <value>The season identifier.</value>
- public string SeasonId { get; set; }
- /// <summary>
- /// Gets or sets the series identifier.
- /// </summary>
- /// <value>The series identifier.</value>
- public string SeriesId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is missing.
- /// </summary>
- /// <value><c>null</c> if [is missing] contains no value, <c>true</c> if [is missing]; otherwise, <c>false</c>.</value>
- public bool? IsMissing { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is virtual unaired.
- /// </summary>
- /// <value><c>null</c> if [is virtual unaired] contains no value, <c>true</c> if [is virtual unaired]; otherwise, <c>false</c>.</value>
- public bool? IsVirtualUnaired { get; set; }
- /// <summary>
- /// Gets or sets the season number.
- /// </summary>
- /// <value>The season number.</value>
- public int? SeasonNumber { get; set; }
- /// <summary>
- /// Gets or sets the fields.
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- /// <summary>
- /// Gets or sets the start item identifier.
- /// </summary>
- /// <value>The start item identifier.</value>
- public string StartItemId { get; set; }
-
- public EpisodeQuery()
- {
- Fields = new ItemFields[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemCountsQuery.cs b/MediaBrowser.Model/Querying/ItemCountsQuery.cs
deleted file mode 100644
index 0bf681537..000000000
--- a/MediaBrowser.Model/Querying/ItemCountsQuery.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class ItemCountsQuery
- /// </summary>
- public class ItemCountsQuery
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
- public bool? IsFavorite { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
deleted file mode 100644
index f9829c329..000000000
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Used to control the data that gets attached to DtoBaseItems
- /// </summary>
- public enum ItemFields
- {
- /// <summary>
- /// The air time
- /// </summary>
- AirTime,
-
- /// <summary>
- /// The alternate episode numbers
- /// </summary>
- AlternateEpisodeNumbers,
-
- /// <summary>
- /// The can delete
- /// </summary>
- CanDelete,
-
- /// <summary>
- /// The can download
- /// </summary>
- CanDownload,
-
- /// <summary>
- /// The channel information
- /// </summary>
- ChannelInfo,
-
- /// <summary>
- /// The chapters
- /// </summary>
- Chapters,
-
- ChildCount,
-
- /// <summary>
- /// The cumulative run time ticks
- /// </summary>
- CumulativeRunTimeTicks,
-
- /// <summary>
- /// The custom rating
- /// </summary>
- CustomRating,
-
- /// <summary>
- /// The date created of the item
- /// </summary>
- DateCreated,
-
- /// <summary>
- /// The date last media added
- /// </summary>
- DateLastMediaAdded,
-
- /// <summary>
- /// Item display preferences
- /// </summary>
- DisplayPreferencesId,
-
- /// <summary>
- /// The etag
- /// </summary>
- Etag,
-
- /// <summary>
- /// The external urls
- /// </summary>
- ExternalUrls,
-
- /// <summary>
- /// Genres
- /// </summary>
- Genres,
-
- /// <summary>
- /// The home page URL
- /// </summary>
- HomePageUrl,
-
- /// <summary>
- /// The item counts
- /// </summary>
- ItemCounts,
-
- /// <summary>
- /// The media source count
- /// </summary>
- MediaSourceCount,
-
- /// <summary>
- /// The media versions
- /// </summary>
- MediaSources,
-
- OriginalTitle,
-
- /// <summary>
- /// The item overview
- /// </summary>
- Overview,
-
- /// <summary>
- /// The id of the item's parent
- /// </summary>
- ParentId,
-
- /// <summary>
- /// The physical path of the item
- /// </summary>
- Path,
-
- /// <summary>
- /// The list of people for the item
- /// </summary>
- People,
-
- PlayAccess,
-
- /// <summary>
- /// The production locations
- /// </summary>
- ProductionLocations,
-
- /// <summary>
- /// Imdb, tmdb, etc
- /// </summary>
- ProviderIds,
-
- /// <summary>
- /// The aspect ratio of the primary image
- /// </summary>
- PrimaryImageAspectRatio,
-
- RecursiveItemCount,
-
- /// <summary>
- /// The settings
- /// </summary>
- Settings,
-
- /// <summary>
- /// The screenshot image tags
- /// </summary>
- ScreenshotImageTags,
-
- SeriesPrimaryImage,
-
- /// <summary>
- /// The series studio
- /// </summary>
- SeriesStudio,
-
- /// <summary>
- /// The sort name of the item
- /// </summary>
- SortName,
-
- /// <summary>
- /// The special episode numbers
- /// </summary>
- SpecialEpisodeNumbers,
-
- /// <summary>
- /// The studios of the item
- /// </summary>
- Studios,
-
- BasicSyncInfo,
- /// <summary>
- /// The synchronize information
- /// </summary>
- SyncInfo,
-
- /// <summary>
- /// The taglines of the item
- /// </summary>
- Taglines,
-
- /// <summary>
- /// The tags
- /// </summary>
- Tags,
-
- /// <summary>
- /// The trailer url of the item
- /// </summary>
- RemoteTrailers,
-
- /// <summary>
- /// The media streams
- /// </summary>
- MediaStreams,
-
- /// <summary>
- /// The season user data
- /// </summary>
- SeasonUserData,
-
- /// <summary>
- /// The service name
- /// </summary>
- ServiceName,
- ThemeSongIds,
- ThemeVideoIds,
- ExternalEtag,
- PresentationUniqueKey,
- InheritedParentalRatingValue,
- ExternalSeriesId,
- SeriesPresentationUniqueKey,
- DateLastRefreshed,
- DateLastSaved,
- RefreshState,
- ChannelImage
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemFilter.cs b/MediaBrowser.Model/Querying/ItemFilter.cs
deleted file mode 100644
index ff28bd08c..000000000
--- a/MediaBrowser.Model/Querying/ItemFilter.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Enum ItemFilter
- /// </summary>
- public enum ItemFilter
- {
- /// <summary>
- /// The item is a folder
- /// </summary>
- IsFolder = 1,
- /// <summary>
- /// The item is not folder
- /// </summary>
- IsNotFolder = 2,
- /// <summary>
- /// The item is unplayed
- /// </summary>
- IsUnplayed = 3,
- /// <summary>
- /// The item is played
- /// </summary>
- IsPlayed = 4,
- /// <summary>
- /// The item is a favorite
- /// </summary>
- IsFavorite = 5,
- /// <summary>
- /// The item is resumable
- /// </summary>
- IsResumable = 7,
- /// <summary>
- /// The likes
- /// </summary>
- Likes = 8,
- /// <summary>
- /// The dislikes
- /// </summary>
- Dislikes = 9,
- /// <summary>
- /// The is favorite or likes
- /// </summary>
- IsFavoriteOrLikes = 10
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
deleted file mode 100644
index 66bdc8aa5..000000000
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// These represent sort orders that are known by the core
- /// </summary>
- public static class ItemSortBy
- {
- public const string AiredEpisodeOrder = "AiredEpisodeOrder";
- /// <summary>
- /// The album
- /// </summary>
- public const string Album = "Album";
- /// <summary>
- /// The album artist
- /// </summary>
- public const string AlbumArtist = "AlbumArtist";
- /// <summary>
- /// The artist
- /// </summary>
- public const string Artist = "Artist";
- /// <summary>
- /// The date created
- /// </summary>
- public const string DateCreated = "DateCreated";
- /// <summary>
- /// The official rating
- /// </summary>
- public const string OfficialRating = "OfficialRating";
- /// <summary>
- /// The date played
- /// </summary>
- public const string DatePlayed = "DatePlayed";
- /// <summary>
- /// The premiere date
- /// </summary>
- public const string PremiereDate = "PremiereDate";
- public const string StartDate = "StartDate";
- /// <summary>
- /// The sort name
- /// </summary>
- public const string SortName = "SortName";
- public const string Name = "Name";
- /// <summary>
- /// The random
- /// </summary>
- public const string Random = "Random";
- /// <summary>
- /// The runtime
- /// </summary>
- public const string Runtime = "Runtime";
- /// <summary>
- /// The community rating
- /// </summary>
- public const string CommunityRating = "CommunityRating";
- /// <summary>
- /// The production year
- /// </summary>
- public const string ProductionYear = "ProductionYear";
- /// <summary>
- /// The play count
- /// </summary>
- public const string PlayCount = "PlayCount";
- /// <summary>
- /// The critic rating
- /// </summary>
- public const string CriticRating = "CriticRating";
- public const string IsFolder = "IsFolder";
- public const string IsUnplayed = "IsUnplayed";
- public const string IsPlayed = "IsPlayed";
- public const string SeriesSortName = "SeriesSortName";
- public const string VideoBitRate = "VideoBitRate";
- public const string AirTime = "AirTime";
- public const string Studio = "Studio";
- public const string Players = "Players";
- public const string GameSystem = "GameSystem";
- public const string IsFavoriteOrLiked = "IsFavoriteOrLiked";
- public const string DateLastContentAdded = "DateLastContentAdded";
- public const string SeriesDatePlayed = "SeriesDatePlayed";
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemsByNameQuery.cs b/MediaBrowser.Model/Querying/ItemsByNameQuery.cs
deleted file mode 100644
index 578f22f60..000000000
--- a/MediaBrowser.Model/Querying/ItemsByNameQuery.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class ItemsByNameQuery
- /// </summary>
- public class ItemsByNameQuery
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
- /// <summary>
- /// Gets or sets the size of the page.
- /// </summary>
- /// <value>The size of the page.</value>
- public int? Limit { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="ItemsByNameQuery" /> is recursive.
- /// </summary>
- /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
- public bool Recursive { get; set; }
- /// <summary>
- /// Gets or sets the sort order.
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder? SortOrder { get; set; }
- /// <summary>
- /// Gets or sets the parent id.
- /// </summary>
- /// <value>The parent id.</value>
- public string ParentId { 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 filters.
- /// </summary>
- /// <value>The filters.</value>
- public ItemFilter[] Filters { 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>
- /// Gets or sets the media types.
- /// </summary>
- /// <value>The media types.</value>
- public string[] MediaTypes { 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 image types.
- /// </summary>
- /// <value>The image types.</value>
- public ImageType[] ImageTypes { 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 less than.
- /// </summary>
- /// <value>The name less than.</value>
- public string NameLessThan { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is played.
- /// </summary>
- /// <value><c>null</c> if [is played] contains no value, <c>true</c> if [is played]; otherwise, <c>false</c>.</value>
- public bool? IsPlayed { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [enable images].
- /// </summary>
- /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
- public bool? EnableImages { get; set; }
- /// <summary>
- /// Gets or sets the image type limit.
- /// </summary>
- /// <value>The image type limit.</value>
- public int? ImageTypeLimit { get; set; }
- /// <summary>
- /// Gets or sets the enable image types.
- /// </summary>
- /// <value>The enable image types.</value>
- public ImageType[] EnableImageTypes { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ItemsByNameQuery" /> class.
- /// </summary>
- public ItemsByNameQuery()
- {
- ImageTypes = new ImageType[] { };
- Filters = new ItemFilter[] { };
- Fields = new ItemFields[] { };
- Recursive = true;
- MediaTypes = new string[] { };
- SortBy = new string[] { };
- ExcludeItemTypes = new string[] { };
- IncludeItemTypes = new string[] { };
- EnableImageTypes = new ImageType[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
deleted file mode 100644
index a8086e5cd..000000000
--- a/MediaBrowser.Model/Querying/LatestItemsQuery.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Querying
-{
- public class LatestItemsQuery
- {
- /// <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>
- /// 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 include item types.
- /// </summary>
- /// <value>The include item types.</value>
- public string[] IncludeItemTypes { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is played.
- /// </summary>
- /// <value><c>null</c> if [is played] contains no value, <c>true</c> if [is played]; otherwise, <c>false</c>.</value>
- public bool? IsPlayed { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [group items].
- /// </summary>
- /// <value><c>true</c> if [group items]; otherwise, <c>false</c>.</value>
- public bool GroupItems { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [enable images].
- /// </summary>
- /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
- public bool? EnableImages { get; set; }
- /// <summary>
- /// Gets or sets the image type limit.
- /// </summary>
- /// <value>The image type limit.</value>
- public int? ImageTypeLimit { get; set; }
- /// <summary>
- /// Gets or sets the enable image types.
- /// </summary>
- /// <value>The enable image types.</value>
- public ImageType[] EnableImageTypes { get; set; }
-
- public LatestItemsQuery()
- {
- EnableImageTypes = new ImageType[] {};
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
deleted file mode 100644
index 91417a4a7..000000000
--- a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- public class MovieRecommendationQuery
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the parent identifier.
- /// </summary>
- /// <value>The parent identifier.</value>
- public string ParentId { get; set; }
- /// <summary>
- /// Gets or sets the item limit.
- /// </summary>
- /// <value>The item limit.</value>
- public int ItemLimit { get; set; }
- /// <summary>
- /// Gets or sets the category limit.
- /// </summary>
- /// <value>The category limit.</value>
- public int CategoryLimit { get; set; }
- /// <summary>
- /// Gets or sets the fields.
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
-
- public MovieRecommendationQuery()
- {
- ItemLimit = 10;
- CategoryLimit = 6;
- Fields = new ItemFields[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
deleted file mode 100644
index e3b0d578a..000000000
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Querying
-{
- public class NextUpQuery
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent identifier.
- /// </summary>
- /// <value>The parent identifier.</value>
- public string ParentId { get; set; }
-
- /// <summary>
- /// Gets or sets the series id.
- /// </summary>
- /// <value>The series id.</value>
- public string SeriesId { 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>
- /// 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 a value indicating whether [enable images].
- /// </summary>
- /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
- public bool? EnableImages { get; set; }
- /// <summary>
- /// Gets or sets the image type limit.
- /// </summary>
- /// <value>The image type limit.</value>
- public int? ImageTypeLimit { get; set; }
- /// <summary>
- /// Gets or sets the enable image types.
- /// </summary>
- /// <value>The enable image types.</value>
- public ImageType[] EnableImageTypes { get; set; }
-
- public bool EnableTotalRecordCount { get; set; }
-
- public NextUpQuery()
- {
- EnableImageTypes = new ImageType[] {};
- EnableTotalRecordCount = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/PersonsQuery.cs b/MediaBrowser.Model/Querying/PersonsQuery.cs
deleted file mode 100644
index a4b7eab71..000000000
--- a/MediaBrowser.Model/Querying/PersonsQuery.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class PersonsQuery
- /// </summary>
- public class PersonsQuery : ItemsByNameQuery
- {
- /// <summary>
- /// Gets or sets the person types.
- /// </summary>
- /// <value>The person types.</value>
- public string[] PersonTypes { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PersonsQuery"/> class.
- /// </summary>
- public PersonsQuery()
- {
- PersonTypes = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs
deleted file mode 100644
index 3a261857b..000000000
--- a/MediaBrowser.Model/Querying/QueryFilters.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Querying
-{
- public class QueryFiltersLegacy
- {
- public string[] Genres { get; set; }
- public string[] Tags { get; set; }
- public string[] OfficialRatings { get; set; }
- public int[] Years { get; set; }
-
- public QueryFiltersLegacy()
- {
- Genres = new string[] { };
- Tags = new string[] { };
- OfficialRatings = new string[] { };
- Years = new int[] { };
- }
- }
- public class QueryFilters
- {
- public NameIdPair[] Genres { get; set; }
- public string[] Tags { get; set; }
-
- public QueryFilters()
- {
- Tags = new string[] { };
- Genres = new NameIdPair[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs
deleted file mode 100644
index 6f9923d08..000000000
--- a/MediaBrowser.Model/Querying/QueryResult.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- public class QueryResult<T>
- {
- /// <summary>
- /// Gets or sets the items.
- /// </summary>
- /// <value>The items.</value>
- public T[] Items { get; set; }
-
- /// <summary>
- /// The total number of records available
- /// </summary>
- /// <value>The total record count.</value>
- public int TotalRecordCount { get; set; }
-
- public QueryResult()
- {
- Items = new T[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Querying/SessionQuery.cs b/MediaBrowser.Model/Querying/SessionQuery.cs
deleted file mode 100644
index fa7df315c..000000000
--- a/MediaBrowser.Model/Querying/SessionQuery.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class SessionQuery
- /// </summary>
- public class SessionQuery
- {
- /// <summary>
- /// Filter by sessions that are allowed to be controlled by a given user
- /// </summary>
- public string ControllableByUserId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs
deleted file mode 100644
index 0dd491550..000000000
--- a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- public class SimilarItemsQuery
- {
- /// <summary>
- /// The user to localize search results for
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/ThemeMediaResult.cs b/MediaBrowser.Model/Querying/ThemeMediaResult.cs
deleted file mode 100644
index 0d7eb502f..000000000
--- a/MediaBrowser.Model/Querying/ThemeMediaResult.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class ThemeMediaResult
- /// </summary>
- public class ThemeMediaResult : QueryResult<BaseItemDto>
- {
- /// <summary>
- /// Gets or sets the owner id.
- /// </summary>
- /// <value>The owner id.</value>
- public string OwnerId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
deleted file mode 100644
index 665b980eb..000000000
--- a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Querying
-{
- public class UpcomingEpisodesQuery
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the parent identifier.
- /// </summary>
- /// <value>The parent identifier.</value>
- public string ParentId { get; set; }
-
- /// <summary>
- /// 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>
- /// 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 a value indicating whether [enable images].
- /// </summary>
- /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
- public bool? EnableImages { get; set; }
- /// <summary>
- /// Gets or sets the image type limit.
- /// </summary>
- /// <value>The image type limit.</value>
- public int? ImageTypeLimit { get; set; }
- /// <summary>
- /// Gets or sets the enable image types.
- /// </summary>
- /// <value>The enable image types.</value>
- public ImageType[] EnableImageTypes { get; set; }
-
- public UpcomingEpisodesQuery()
- {
- EnableImageTypes = new ImageType[] {};
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Querying/UserQuery.cs b/MediaBrowser.Model/Querying/UserQuery.cs
deleted file mode 100644
index 48dbd30aa..000000000
--- a/MediaBrowser.Model/Querying/UserQuery.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Querying
-{
- public class UserQuery
- {
- public bool? IsHidden { get; set; }
- public bool? IsDisabled { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs
deleted file mode 100644
index e8e9c414c..000000000
--- a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.IO;
-using System.Reflection;
-
-namespace MediaBrowser.Model.Reflection
-{
- public interface IAssemblyInfo
- {
- Stream GetManifestResourceStream(Type type, string resource);
- string[] GetManifestResourceNames(Type type);
-
- Assembly[] GetCurrentAssemblies();
- }
-}
diff --git a/MediaBrowser.Model/Registration/RegistrationInfo.cs b/MediaBrowser.Model/Registration/RegistrationInfo.cs
deleted file mode 100644
index 4e97989cf..000000000
--- a/MediaBrowser.Model/Registration/RegistrationInfo.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Registration
-{
- public class RegistrationInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the expiration date.
- /// </summary>
- /// <value>The expiration date.</value>
- public DateTime ExpirationDate { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is trial.
- /// </summary>
- /// <value><c>true</c> if this instance is trial; otherwise, <c>false</c>.</value>
- public bool IsTrial { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is registered.
- /// </summary>
- /// <value><c>true</c> if this instance is registered; otherwise, <c>false</c>.</value>
- public bool IsRegistered { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs
deleted file mode 100644
index f2617c909..000000000
--- a/MediaBrowser.Model/Search/SearchHint.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Search
-{
- /// <summary>
- /// Class SearchHintResult
- /// </summary>
- public class SearchHint
- {
- /// <summary>
- /// Gets or sets the item id.
- /// </summary>
- /// <value>The item id.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the matched term.
- /// </summary>
- /// <value>The matched term.</value>
- public string MatchedTerm { get; set; }
-
- /// <summary>
- /// Gets or sets the index number.
- /// </summary>
- /// <value>The index number.</value>
- public int? IndexNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the production year.
- /// </summary>
- /// <value>The production year.</value>
- public int? ProductionYear { 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 image tag.
- /// </summary>
- /// <value>The image tag.</value>
- public string PrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the thumb image tag.
- /// </summary>
- /// <value>The thumb image tag.</value>
- public string ThumbImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the thumb image item identifier.
- /// </summary>
- /// <value>The thumb image item identifier.</value>
- public string ThumbImageItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the backdrop image tag.
- /// </summary>
- /// <value>The backdrop image tag.</value>
- public string BackdropImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the backdrop image item identifier.
- /// </summary>
- /// <value>The backdrop image item identifier.</value>
- public string BackdropImageItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- public long? RunTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the media.
- /// </summary>
- /// <value>The type of the media.</value>
- public string MediaType { get; set; }
-
- public DateTime? StartDate { get; set; }
- public DateTime? EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the series.
- /// </summary>
- /// <value>The series.</value>
- public string Series { get; set; }
-
- public string Status { get; set; }
-
- /// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- public string Album { get; set; }
- public string AlbumId { get; set; }
-
- /// <summary>
- /// Gets or sets the album artist.
- /// </summary>
- /// <value>The album artist.</value>
- public string AlbumArtist { get; set; }
-
- /// <summary>
- /// Gets or sets the artists.
- /// </summary>
- /// <value>The artists.</value>
- public string[] Artists { get; set; }
-
- /// <summary>
- /// Gets or sets the song count.
- /// </summary>
- /// <value>The song count.</value>
- public int? SongCount { get; set; }
-
- /// <summary>
- /// Gets or sets the episode count.
- /// </summary>
- /// <value>The episode count.</value>
- public int? EpisodeCount { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the channel.
- /// </summary>
- /// <value>The name of the channel.</value>
- public string ChannelName { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image aspect ratio.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- public double? PrimaryImageAspectRatio { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Search/SearchHintResult.cs b/MediaBrowser.Model/Search/SearchHintResult.cs
deleted file mode 100644
index 372528f82..000000000
--- a/MediaBrowser.Model/Search/SearchHintResult.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Search
-{
- /// <summary>
- /// Class SearchHintResult
- /// </summary>
- public class SearchHintResult
- {
- /// <summary>
- /// Gets or sets the search hints.
- /// </summary>
- /// <value>The search hints.</value>
- public SearchHint[] SearchHints { get; set; }
-
- /// <summary>
- /// Gets or sets the total record count.
- /// </summary>
- /// <value>The total record count.</value>
- public int TotalRecordCount { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs
deleted file mode 100644
index 0081591ca..000000000
--- a/MediaBrowser.Model/Search/SearchQuery.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-
-namespace MediaBrowser.Model.Search
-{
- public class SearchQuery
- {
- /// <summary>
- /// The user to localize search results for
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the search term.
- /// </summary>
- /// <value>The search term.</value>
- public string SearchTerm { 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; }
-
- public bool IncludePeople { get; set; }
- public bool IncludeMedia { get; set; }
- public bool IncludeGenres { get; set; }
- public bool IncludeStudios { get; set; }
- public bool IncludeArtists { get; set; }
-
- public string[] MediaTypes { get; set; }
- public string[] IncludeItemTypes { get; set; }
- public string[] ExcludeItemTypes { get; set; }
- public string ParentId { get; set; }
-
- public bool? IsMovie { get; set; }
-
- public bool? IsSeries { get; set; }
-
- public bool? IsNews { get; set; }
-
- public bool? IsKids { get; set; }
-
- public bool? IsSports { get; set; }
-
- public SearchQuery()
- {
- IncludeArtists = true;
- IncludeGenres = true;
- IncludeMedia = true;
- IncludePeople = true;
- IncludeStudios = true;
-
- MediaTypes = new string[] { };
- IncludeItemTypes = new string[] { };
- ExcludeItemTypes = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
deleted file mode 100644
index cd1e550e5..000000000
--- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using System.IO;
-
-namespace MediaBrowser.Model.Serialization
-{
- public interface IJsonSerializer
- {
- /// <summary>
- /// Serializes to stream.
- /// </summary>
- /// <param name="obj">The obj.</param>
- /// <param name="stream">The stream.</param>
- /// <exception cref="System.ArgumentNullException">obj</exception>
- void SerializeToStream(object obj, Stream stream);
-
- /// <summary>
- /// Serializes to file.
- /// </summary>
- /// <param name="obj">The obj.</param>
- /// <param name="file">The file.</param>
- /// <exception cref="System.ArgumentNullException">obj</exception>
- void SerializeToFile(object obj, string file);
-
- /// <summary>
- /// Deserializes from file.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="file">The file.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">type</exception>
- object DeserializeFromFile(Type type, string file);
-
- /// <summary>
- /// Deserializes from file.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="file">The file.</param>
- /// <returns>``0.</returns>
- /// <exception cref="System.ArgumentNullException">file</exception>
- T DeserializeFromFile<T>(string file)
- where T : class;
-
- /// <summary>
- /// Deserializes from stream.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="stream">The stream.</param>
- /// <returns>``0.</returns>
- /// <exception cref="System.ArgumentNullException">stream</exception>
- T DeserializeFromStream<T>(Stream stream);
-
- /// <summary>
- /// Deserializes from string.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="text">The text.</param>
- /// <returns>``0.</returns>
- /// <exception cref="System.ArgumentNullException">text</exception>
- T DeserializeFromString<T>(string text);
-
- /// <summary>
- /// Deserializes from stream.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <param name="type">The type.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">stream</exception>
- object DeserializeFromStream(Stream stream, Type type);
-
- /// <summary>
- /// Deserializes from string.
- /// </summary>
- /// <param name="json">The json.</param>
- /// <param name="type">The type.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">json</exception>
- object DeserializeFromString(string json, Type type);
-
- /// <summary>
- /// Serializes to string.
- /// </summary>
- /// <param name="obj">The obj.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="System.ArgumentNullException">obj</exception>
- string SerializeToString(object obj);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
deleted file mode 100644
index b26b673f3..000000000
--- a/MediaBrowser.Model/Serialization/IXmlSerializer.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-using System.IO;
-
-namespace MediaBrowser.Model.Serialization
-{
- public interface IXmlSerializer
- {
- /// <summary>
- /// Deserializes from stream.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="stream">The stream.</param>
- /// <returns>System.Object.</returns>
- object DeserializeFromStream(Type type, Stream stream);
-
- /// <summary>
- /// Serializes to stream.
- /// </summary>
- /// <param name="obj">The obj.</param>
- /// <param name="stream">The stream.</param>
- void SerializeToStream(object obj, Stream stream);
-
- /// <summary>
- /// Serializes to file.
- /// </summary>
- /// <param name="obj">The obj.</param>
- /// <param name="file">The file.</param>
- void SerializeToFile(object obj, string file);
-
- /// <summary>
- /// Deserializes from file.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="file">The file.</param>
- /// <returns>System.Object.</returns>
- object DeserializeFromFile(Type type, string file);
-
- /// <summary>
- /// Deserializes from bytes.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="buffer">The buffer.</param>
- /// <returns>System.Object.</returns>
- object DeserializeFromBytes(Type type, byte[] buffer);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs b/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs
deleted file mode 100644
index 8e23edc24..000000000
--- a/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Serialization
-{
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
- public sealed class IgnoreDataMemberAttribute : Attribute
- {
- public IgnoreDataMemberAttribute()
- {
- }
- }
-}
diff --git a/MediaBrowser.Model/Services/ApiMemberAttribute.cs b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
deleted file mode 100644
index 4a2831775..000000000
--- a/MediaBrowser.Model/Services/ApiMemberAttribute.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Services
-{
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
- public class ApiMemberAttribute : Attribute
- {
- /// <summary>
- /// Gets or sets verb to which applies attribute. By default applies to all verbs.
- /// </summary>
- public string Verb { get; set; }
-
- /// <summary>
- /// Gets or sets parameter type: It can be only one of the following: path, query, body, form, or header.
- /// </summary>
- public string ParameterType { get; set; }
-
- /// <summary>
- /// Gets or sets unique name for the parameter. Each name must be unique, even if they are associated with different paramType values.
- /// </summary>
- /// <remarks>
- /// <para>
- /// Other notes on the name field:
- /// If paramType is body, the name is used only for UI and codegeneration.
- /// If paramType is path, the name field must correspond to the associated path segment from the path field in the api object.
- /// If paramType is query, the name field corresponds to the query param name.
- /// </para>
- /// </remarks>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the human-readable description for the parameter.
- /// </summary>
- public string Description { get; set; }
-
- /// <summary>
- /// For path, query, and header paramTypes, this field must be a primitive. For body, this can be a complex or container datatype.
- /// </summary>
- public string DataType { get; set; }
-
- /// <summary>
- /// For path, this is always true. Otherwise, this field tells the client whether or not the field must be supplied.
- /// </summary>
- public bool IsRequired { get; set; }
-
- /// <summary>
- /// For query params, this specifies that a comma-separated list of values can be passed to the API. For path and body types, this field cannot be true.
- /// </summary>
- public bool AllowMultiple { get; set; }
-
- /// <summary>
- /// Gets or sets route to which applies attribute, matches using StartsWith. By default applies to all routes.
- /// </summary>
- public string Route { get; set; }
-
- /// <summary>
- /// Whether to exclude this property from being included in the ModelSchema
- /// </summary>
- public bool ExcludeInSchema { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
deleted file mode 100644
index b10e12813..000000000
--- a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IAsyncStreamWriter
- {
- Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Model/Services/IHasHeaders.cs b/MediaBrowser.Model/Services/IHasHeaders.cs
deleted file mode 100644
index 35e652b0f..000000000
--- a/MediaBrowser.Model/Services/IHasHeaders.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IHasHeaders
- {
- IDictionary<string, string> Headers { get; }
- }
-}
diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs
deleted file mode 100644
index 2164179d5..000000000
--- a/MediaBrowser.Model/Services/IHasRequestFilter.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-
-namespace MediaBrowser.Model.Services
-{
- public interface IHasRequestFilter
- {
- /// <summary>
- /// Order in which Request Filters are executed.
- /// &lt;0 Executed before global request filters
- /// &gt;0 Executed after global request filters
- /// </summary>
- int Priority { get; }
-
- /// <summary>
- /// The request filter is executed before the service.
- /// </summary>
- /// <param name="req">The http request wrapper</param>
- /// <param name="res">The http response wrapper</param>
- /// <param name="requestDto">The request DTO</param>
- void RequestFilter(IRequest req, IResponse res, object requestDto);
- }
-}
diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs
deleted file mode 100644
index e1480f30a..000000000
--- a/MediaBrowser.Model/Services/IHttpRequest.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IHttpRequest : IRequest
- {
- /// <summary>
- /// The HttpResponse
- /// </summary>
- IHttpResponse HttpResponse { get; }
-
- /// <summary>
- /// The HTTP Verb
- /// </summary>
- string HttpMethod { get; }
-
- /// <summary>
- /// The IP Address of the X-Forwarded-For header, null if null or empty
- /// </summary>
- string XForwardedFor { get; }
-
- /// <summary>
- /// The Port number of the X-Forwarded-Port header, null if null or empty
- /// </summary>
- int? XForwardedPort { get; }
-
- /// <summary>
- /// The http or https scheme of the X-Forwarded-Proto header, null if null or empty
- /// </summary>
- string XForwardedProtocol { get; }
-
- /// <summary>
- /// The value of the X-Real-IP header, null if null or empty
- /// </summary>
- string XRealIp { get; }
-
- /// <summary>
- /// The value of the Accept HTTP Request Header
- /// </summary>
- string Accept { get; }
- }
-}
diff --git a/MediaBrowser.Model/Services/IHttpResponse.cs b/MediaBrowser.Model/Services/IHttpResponse.cs
deleted file mode 100644
index cd9c07d46..000000000
--- a/MediaBrowser.Model/Services/IHttpResponse.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IHttpResponse : IResponse
- {
- //ICookies Cookies { get; }
-
- /// <summary>
- /// Adds a new Set-Cookie instruction to Response
- /// </summary>
- /// <param name="cookie"></param>
- void SetCookie(Cookie cookie);
-
- /// <summary>
- /// Removes all pending Set-Cookie instructions
- /// </summary>
- void ClearCookies();
- }
-}
diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs
deleted file mode 100644
index 90afb0f27..000000000
--- a/MediaBrowser.Model/Services/IHttpResult.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IHttpResult : IHasHeaders
- {
- /// <summary>
- /// The HTTP Response Status
- /// </summary>
- int Status { get; set; }
-
- /// <summary>
- /// The HTTP Response Status Code
- /// </summary>
- HttpStatusCode StatusCode { get; set; }
-
- /// <summary>
- /// The HTTP Response ContentType
- /// </summary>
- string ContentType { get; set; }
-
- /// <summary>
- /// Additional HTTP Cookies
- /// </summary>
- List<Cookie> Cookies { get; }
-
- /// <summary>
- /// Response DTO
- /// </summary>
- object Response { get; set; }
-
- /// <summary>
- /// Holds the request call context
- /// </summary>
- IRequest RequestContext { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs
deleted file mode 100644
index 5a895815e..000000000
--- a/MediaBrowser.Model/Services/IRequest.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IRequest
- {
- /// <summary>
- /// The underlying ASP.NET or HttpListener HttpRequest
- /// </summary>
- object OriginalRequest { get; }
-
- IResponse Response { get; }
-
- /// <summary>
- /// The name of the service being called (e.g. Request DTO Name)
- /// </summary>
- string OperationName { get; set; }
-
- /// <summary>
- /// The Verb / HttpMethod or Action for this request
- /// </summary>
- string Verb { get; }
-
- /// <summary>
- /// The Request DTO, after it has been deserialized.
- /// </summary>
- object Dto { get; set; }
-
- /// <summary>
- /// The request ContentType
- /// </summary>
- string ContentType { get; }
-
- bool IsLocal { get; }
-
- string UserAgent { get; }
-
- IDictionary<string, Cookie> Cookies { get; }
-
- /// <summary>
- /// The expected Response ContentType for this request
- /// </summary>
- string ResponseContentType { get; set; }
-
- /// <summary>
- /// Attach any data to this request that all filters and services can access.
- /// </summary>
- Dictionary<string, object> Items { get; }
-
- QueryParamCollection Headers { get; }
-
- QueryParamCollection QueryString { get; }
-
- QueryParamCollection FormData { get; }
-
- string RawUrl { get; }
-
- string AbsoluteUri { get; }
-
- /// <summary>
- /// The Remote Ip as reported by Request.UserHostAddress
- /// </summary>
- string UserHostAddress { get; }
-
- /// <summary>
- /// The Remote Ip as reported by X-Forwarded-For, X-Real-IP or Request.UserHostAddress
- /// </summary>
- string RemoteIp { get; }
-
- /// <summary>
- /// The value of the Authorization Header used to send the Api Key, null if not available
- /// </summary>
- string Authorization { get; }
-
- /// <summary>
- /// e.g. is https or not
- /// </summary>
- bool IsSecureConnection { get; }
-
- string[] AcceptTypes { get; }
-
- string PathInfo { get; }
-
- Stream InputStream { get; }
-
- long ContentLength { get; }
-
- /// <summary>
- /// Access to the multi-part/formdata files posted on this request
- /// </summary>
- IHttpFile[] Files { get; }
-
- /// <summary>
- /// The value of the Referrer, null if not available
- /// </summary>
- Uri UrlReferrer { get; }
- }
-
- public interface IHttpFile
- {
- string Name { get; }
- string FileName { get; }
- long ContentLength { get; }
- string ContentType { get; }
- Stream InputStream { get; }
- }
-
- public interface IRequiresRequest
- {
- IRequest Request { get; set; }
- }
-
- public interface IResponse
- {
- IRequest Request { get; }
-
- int StatusCode { get; set; }
-
- string StatusDescription { get; set; }
-
- string ContentType { get; set; }
-
- void AddHeader(string name, string value);
-
- string GetHeader(string name);
-
- void Redirect(string url);
-
- Stream OutputStream { get; }
-
- /// <summary>
- /// Signal that this response has been handled and no more processing should be done.
- /// When used in a request or response filter, no more filters or processing is done on this request.
- /// </summary>
- void Close();
-
- /// <summary>
- /// Gets a value indicating whether this instance is closed.
- /// </summary>
- bool IsClosed { get; }
-
- void SetContentLength(long contentLength);
-
- //Add Metadata to Response
- Dictionary<string, object> Items { get; }
-
- QueryParamCollection Headers { get; }
-
- Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Model/Services/IRequestFilter.cs b/MediaBrowser.Model/Services/IRequestFilter.cs
deleted file mode 100644
index 7f6db2e4d..000000000
--- a/MediaBrowser.Model/Services/IRequestFilter.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Model.Services
-{
- public interface IRequestFilter
- {
- void Filter(IRequest request, IResponse response, object requestDto);
- }
-}
diff --git a/MediaBrowser.Model/Services/IRequiresRequestStream.cs b/MediaBrowser.Model/Services/IRequiresRequestStream.cs
deleted file mode 100644
index 0b8ac3ed3..000000000
--- a/MediaBrowser.Model/Services/IRequiresRequestStream.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.IO;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IRequiresRequestStream
- {
- /// <summary>
- /// The raw Http Request Input Stream
- /// </summary>
- Stream RequestStream { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Services/IService.cs b/MediaBrowser.Model/Services/IService.cs
deleted file mode 100644
index 3e0ff280b..000000000
--- a/MediaBrowser.Model/Services/IService.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Services
-{
- // marker interface
- public interface IService
- {
- }
-
- public interface IReturn { }
- public interface IReturn<T> : IReturn { }
- public interface IReturnVoid : IReturn { }
-}
diff --git a/MediaBrowser.Model/Services/IStreamWriter.cs b/MediaBrowser.Model/Services/IStreamWriter.cs
deleted file mode 100644
index 1fc11049e..000000000
--- a/MediaBrowser.Model/Services/IStreamWriter.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.IO;
-
-namespace MediaBrowser.Model.Services
-{
- public interface IStreamWriter
- {
- void WriteTo(Stream responseStream);
- }
-}
diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs
deleted file mode 100644
index e13e5feca..000000000
--- a/MediaBrowser.Model/Services/QueryParamCollection.cs
+++ /dev/null
@@ -1,229 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Model.Services
-{
- public class QueryParamCollection : List<NameValuePair>
- {
- public QueryParamCollection()
- {
-
- }
-
- public QueryParamCollection(IDictionary<string, string> headers)
- {
- foreach (var pair in headers)
- {
- Add(pair.Key, pair.Value);
- }
- }
-
- private StringComparison GetStringComparison()
- {
- return StringComparison.OrdinalIgnoreCase;
- }
-
- private StringComparer GetStringComparer()
- {
- return StringComparer.OrdinalIgnoreCase;
- }
-
- public string GetKey(int index)
- {
- return this[index].Name;
- }
-
- public string Get(int index)
- {
- return this[index].Value;
- }
-
- public virtual string[] GetValues(int index)
- {
- return new[] { Get(index) };
- }
-
- /// <summary>
- /// Adds a new query parameter.
- /// </summary>
- public virtual void Add(string key, string value)
- {
- Add(new NameValuePair(key, value));
- }
-
- public virtual void Set(string key, string value)
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- var parameters = GetItems(key);
-
- foreach (var p in parameters)
- {
- Remove(p);
- }
-
- return;
- }
-
- foreach (var pair in this)
- {
- var stringComparison = GetStringComparison();
-
- if (string.Equals(key, pair.Name, stringComparison))
- {
- pair.Value = value;
- return;
- }
- }
-
- Add(key, value);
- }
-
- /// <summary>
- /// Removes all parameters of the given name.
- /// </summary>
- /// <returns>The number of parameters that were removed</returns>
- /// <exception cref="ArgumentNullException"><paramref name="name" /> is null.</exception>
- public virtual int Remove(string name)
- {
- return RemoveAll(p => p.Name == name);
- }
-
- public string Get(string name)
- {
- var stringComparison = GetStringComparison();
-
- foreach (var pair in this)
- {
- if (string.Equals(pair.Name, name, stringComparison))
- {
- return pair.Value;
- }
- }
-
- return null;
- }
-
- public virtual List<NameValuePair> GetItems(string name)
- {
- var stringComparison = GetStringComparison();
-
- var list = new List<NameValuePair>();
-
- foreach (var pair in this)
- {
- if (string.Equals(pair.Name, name, stringComparison))
- {
- list.Add(pair);
- }
- }
-
- return list;
- }
-
- public virtual List<string> GetValues(string name)
- {
- var stringComparison = GetStringComparison();
-
- var list = new List<string>();
-
- foreach (var pair in this)
- {
- if (string.Equals(pair.Name, name, stringComparison))
- {
- list.Add(pair.Value);
- }
- }
-
- return list;
- }
-
- public Dictionary<string, string> ToDictionary()
- {
- var stringComparer = GetStringComparer();
-
- var headers = new Dictionary<string, string>(stringComparer);
-
- foreach (var pair in this)
- {
- headers[pair.Name] = pair.Value;
- }
-
- return headers;
- }
-
- public IEnumerable<string> Keys
- {
- get
- {
- var keys = new string[this.Count];
-
- for (var i = 0; i < keys.Length; i++)
- {
- keys[i] = this[i].Name;
- }
-
- return keys;
- }
- }
-
- /// <summary>
- /// Gets or sets a query parameter value by name. A query may contain multiple values of the same name
- /// (i.e. "x=1&amp;x=2"), in which case the value is an array, which works for both getting and setting.
- /// </summary>
- /// <param name="name">The query parameter name</param>
- /// <returns>The query parameter value or array of values</returns>
- public string this[string name]
- {
- get { return Get(name); }
- set
- {
- Set(name, value);
- //var parameters = this.Where(p => p.Name == name).ToArray();
- //var values = new[] { value };
-
- //for (int i = 0; ; i++)
- //{
- // if (i < parameters.Length && i < values.Length)
- // {
- // if (values[i] == null)
- // Remove(parameters[i]);
- // else if (values[i] is NameValuePair)
- // this[IndexOf(parameters[i])] = (NameValuePair)values[i];
- // else
- // parameters[i].Value = values[i];
- // }
- // else if (i < parameters.Length)
- // Remove(parameters[i]);
- // else if (i < values.Length)
- // {
- // if (values[i] != null)
- // {
- // if (values[i] is NameValuePair)
- // Add((NameValuePair)values[i]);
- // else
- // Add(name, values[i]);
- // }
- // }
- // else
- // break;
- //}
- }
- }
-
- private string GetQueryStringValue(NameValuePair pair)
- {
- return pair.Name + "=" + pair.Value;
- }
-
- public override String ToString()
- {
- var vals = this.Select(GetQueryStringValue).ToArray(this.Count);
-
- return string.Join("&", vals);
- }
- }
-}
diff --git a/MediaBrowser.Model/Services/RouteAttribute.cs b/MediaBrowser.Model/Services/RouteAttribute.cs
deleted file mode 100644
index 264500e60..000000000
--- a/MediaBrowser.Model/Services/RouteAttribute.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Services
-{
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
- public class RouteAttribute : Attribute
- {
- /// <summary>
- /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
- /// </summary>
- /// <param name="path">
- /// <para>The path template to map to the request. See
- /// <see cref="Path">RouteAttribute.Path</see>
- /// for details on the correct format.</para>
- /// </param>
- public RouteAttribute(string path)
- : this(path, null)
- {
- }
-
- /// <summary>
- /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
- /// </summary>
- /// <param name="path">
- /// <para>The path template to map to the request. See
- /// <see cref="Path">RouteAttribute.Path</see>
- /// for details on the correct format.</para>
- /// </param>
- /// <param name="verbs">A comma-delimited list of HTTP verbs supported by the
- /// service. If unspecified, all verbs are assumed to be supported.</param>
- public RouteAttribute(string path, string verbs)
- {
- Path = path;
- Verbs = verbs;
- }
-
- /// <summary>
- /// Gets or sets the path template to be mapped to the request.
- /// </summary>
- /// <value>
- /// A <see cref="String"/> value providing the path mapped to
- /// the request. Never <see langword="null"/>.
- /// </value>
- /// <remarks>
- /// <para>Some examples of valid paths are:</para>
- ///
- /// <list>
- /// <item>"/Inventory"</item>
- /// <item>"/Inventory/{Category}/{ItemId}"</item>
- /// <item>"/Inventory/{ItemPath*}"</item>
- /// </list>
- ///
- /// <para>Variables are specified within "{}"
- /// brackets. Each variable in the path is mapped to the same-named property
- /// on the request DTO. At runtime, ServiceStack will parse the
- /// request URL, extract the variable values, instantiate the request DTO,
- /// and assign the variable values into the corresponding request properties,
- /// prior to passing the request DTO to the service object for processing.</para>
- ///
- /// <para>It is not necessary to specify all request properties as
- /// variables in the path. For unspecified properties, callers may provide
- /// values in the query string. For example: the URL
- /// "http://services/Inventory?Category=Books&amp;ItemId=12345" causes the same
- /// request DTO to be processed as "http://services/Inventory/Books/12345",
- /// provided that the paths "/Inventory" (which supports the first URL) and
- /// "/Inventory/{Category}/{ItemId}" (which supports the second URL)
- /// are both mapped to the request DTO.</para>
- ///
- /// <para>Please note that while it is possible to specify property values
- /// in the query string, it is generally considered to be less RESTful and
- /// less desirable than to specify them as variables in the path. Using the
- /// query string to specify property values may also interfere with HTTP
- /// caching.</para>
- ///
- /// <para>The final variable in the path may contain a "*" suffix
- /// to grab all remaining segments in the path portion of the request URL and assign
- /// them to a single property on the request DTO.
- /// For example, if the path "/Inventory/{ItemPath*}" is mapped to the request DTO,
- /// then the request URL "http://services/Inventory/Books/12345" will result
- /// in a request DTO whose ItemPath property contains "Books/12345".
- /// You may only specify one such variable in the path, and it must be positioned at
- /// the end of the path.</para>
- /// </remarks>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets short summary of what the route does.
- /// </summary>
- public string Summary { get; set; }
-
- public string Description { get; set; }
-
- public bool IsHidden { get; set; }
-
- /// <summary>
- /// Gets or sets longer text to explain the behaviour of the route.
- /// </summary>
- public string Notes { get; set; }
-
- /// <summary>
- /// Gets or sets a comma-delimited list of HTTP verbs supported by the service, such as
- /// "GET,PUT,POST,DELETE".
- /// </summary>
- /// <value>
- /// A <see cref="String"/> providing a comma-delimited list of HTTP verbs supported
- /// by the service, <see langword="null"/> or empty if all verbs are supported.
- /// </value>
- public string Verbs { get; set; }
-
- /// <summary>
- /// Used to rank the precedences of route definitions in reverse routing.
- /// i.e. Priorities below 0 are auto-generated have less precedence.
- /// </summary>
- public int Priority { get; set; }
-
- protected bool Equals(RouteAttribute other)
- {
- return base.Equals(other)
- && string.Equals(Path, other.Path)
- && string.Equals(Summary, other.Summary)
- && string.Equals(Notes, other.Notes)
- && string.Equals(Verbs, other.Verbs)
- && Priority == other.Priority;
- }
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((RouteAttribute)obj);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (Path != null ? Path.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (Summary != null ? Summary.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (Notes != null ? Notes.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (Verbs != null ? Verbs.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ Priority;
- return hashCode;
- }
- }
- }
-}
diff --git a/MediaBrowser.Model/Session/BrowseRequest.cs b/MediaBrowser.Model/Session/BrowseRequest.cs
deleted file mode 100644
index 0a13c0549..000000000
--- a/MediaBrowser.Model/Session/BrowseRequest.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class BrowseRequest
- /// </summary>
- public class BrowseRequest
- {
- /// <summary>
- /// Artist, Genre, Studio, Person, or any kind of BaseItem
- /// </summary>
- /// <value>The type of the item.</value>
- public string ItemType { get; set; }
-
- /// <summary>
- /// Gets or sets the item id.
- /// </summary>
- /// <value>The item id.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the item.
- /// </summary>
- /// <value>The name of the item.</value>
- public string ItemName { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs
deleted file mode 100644
index 9ae1fae9f..000000000
--- a/MediaBrowser.Model/Session/ClientCapabilities.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Session
-{
- public class ClientCapabilities
- {
- public string[] PlayableMediaTypes { get; set; }
-
- public string[] SupportedCommands { get; set; }
-
- public bool SupportsMediaControl { get; set; }
- public bool SupportsContentUploading { get; set; }
- public string MessageCallbackUrl { get; set; }
-
- public bool SupportsPersistentIdentifier { get; set; }
- public bool SupportsSync { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
- public string[] SupportedLiveMediaTypes { get; set; }
-
- public string AppStoreUrl { get; set; }
- public string IconUrl { get; set; }
-
- public ClientCapabilities()
- {
- PlayableMediaTypes = new string[] { };
- SupportedCommands = new string[] { };
- SupportsPersistentIdentifier = true;
- SupportedLiveMediaTypes = new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs
deleted file mode 100644
index ae9ab3adc..000000000
--- a/MediaBrowser.Model/Session/GeneralCommand.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Session
-{
- public class GeneralCommand
- {
- public string Name { get; set; }
-
- public string ControllingUserId { get; set; }
-
- public Dictionary<string, string> Arguments { get; set; }
-
- public GeneralCommand()
- {
- Arguments = new Dictionary<string, string>();
- }
- }
-}
diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs
deleted file mode 100644
index 616d5f9b4..000000000
--- a/MediaBrowser.Model/Session/GeneralCommandType.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// This exists simply to identify a set of known commands.
- /// </summary>
- public enum GeneralCommandType
- {
- MoveUp = 0,
- MoveDown = 1,
- MoveLeft = 2,
- MoveRight = 3,
- PageUp = 4,
- PageDown = 5,
- PreviousLetter = 6,
- NextLetter = 7,
- ToggleOsd = 8,
- ToggleContextMenu = 9,
- Select = 10,
- Back = 11,
- TakeScreenshot = 12,
- SendKey = 13,
- SendString = 14,
- GoHome = 15,
- GoToSettings = 16,
- VolumeUp = 17,
- VolumeDown = 18,
- Mute = 19,
- Unmute = 20,
- ToggleMute = 21,
- SetVolume = 22,
- SetAudioStreamIndex = 23,
- SetSubtitleStreamIndex = 24,
- ToggleFullscreen = 25,
- DisplayContent = 26,
- GoToSearch = 27,
- DisplayMessage = 28,
- SetRepeatMode = 29,
- ChannelUp = 30,
- ChannelDown = 31,
- SetMaxStreamingBitrate = 31,
- Guide = 32,
- ToggleStats = 33,
- PlayMediaSource = 34
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs
deleted file mode 100644
index b028765ed..000000000
--- a/MediaBrowser.Model/Session/MessageCommand.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Session
-{
- public class MessageCommand
- {
- public string Header { get; set; }
-
- public string Text { get; set; }
-
- public long? TimeoutMs { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlayCommand.cs b/MediaBrowser.Model/Session/PlayCommand.cs
deleted file mode 100644
index 3a5a951d7..000000000
--- a/MediaBrowser.Model/Session/PlayCommand.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Enum PlayCommand
- /// </summary>
- public enum PlayCommand
- {
- /// <summary>
- /// The play now
- /// </summary>
- PlayNow = 0,
- /// <summary>
- /// The play next
- /// </summary>
- PlayNext = 1,
- /// <summary>
- /// The play last
- /// </summary>
- PlayLast = 2,
- /// <summary>
- /// The play instant mix
- /// </summary>
- PlayInstantMix = 3,
- /// <summary>
- /// The play shuffle
- /// </summary>
- PlayShuffle = 4
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlayMethod.cs b/MediaBrowser.Model/Session/PlayMethod.cs
deleted file mode 100644
index 87b728627..000000000
--- a/MediaBrowser.Model/Session/PlayMethod.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- public enum PlayMethod
- {
- Transcode = 0,
- DirectStream = 1,
- DirectPlay = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
deleted file mode 100644
index 3477f2e57..000000000
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class PlayRequest
- /// </summary>
- public class PlayRequest
- {
- /// <summary>
- /// Gets or sets the item ids.
- /// </summary>
- /// <value>The item ids.</value>
- [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
- public string[] ItemIds { get; set; }
-
- /// <summary>
- /// Gets or sets the start position ticks that the first item should be played at
- /// </summary>
- /// <value>The start position ticks.</value>
- [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public long? StartPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the play command.
- /// </summary>
- /// <value>The play command.</value>
- [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public PlayCommand PlayCommand { get; set; }
-
- /// <summary>
- /// Gets or sets the controlling user identifier.
- /// </summary>
- /// <value>The controlling user identifier.</value>
- public string ControllingUserId { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
- public int? AudioStreamIndex { get; set; }
- public string MediaSourceId { get; set; }
- public int? StartIndex { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
deleted file mode 100644
index 0319f6711..000000000
--- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class PlaybackProgressInfo.
- /// </summary>
- public class PlaybackProgressInfo
- {
- /// <summary>
- /// Gets or sets a value indicating whether this instance can seek.
- /// </summary>
- /// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
- public bool CanSeek { get; set; }
-
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItemDto Item { get; set; }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the session id.
- /// </summary>
- /// <value>The session id.</value>
- public string SessionId { get; set; }
-
- /// <summary>
- /// Gets or sets the media version identifier.
- /// </summary>
- /// <value>The media version identifier.</value>
- public string MediaSourceId { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the audio stream.
- /// </summary>
- /// <value>The index of the audio stream.</value>
- public int? AudioStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the subtitle stream.
- /// </summary>
- /// <value>The index of the subtitle stream.</value>
- public int? SubtitleStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is paused.
- /// </summary>
- /// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value>
- public bool IsPaused { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is muted.
- /// </summary>
- /// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value>
- public bool IsMuted { get; set; }
-
- /// <summary>
- /// Gets or sets the position ticks.
- /// </summary>
- /// <value>The position ticks.</value>
- public long? PositionTicks { get; set; }
-
- public long? PlaybackStartTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the volume level.
- /// </summary>
- /// <value>The volume level.</value>
- public int? VolumeLevel { get; set; }
-
- public int? Brightness { get; set; }
-
- public string AspectRatio { get; set; }
-
- /// <summary>
- /// Gets or sets the play method.
- /// </summary>
- /// <value>The play method.</value>
- public PlayMethod PlayMethod { get; set; }
- /// <summary>
- /// Gets or sets the live stream identifier.
- /// </summary>
- /// <value>The live stream identifier.</value>
- public string LiveStreamId { get; set; }
- /// <summary>
- /// Gets or sets the play session identifier.
- /// </summary>
- /// <value>The play session identifier.</value>
- public string PlaySessionId { get; set; }
- /// <summary>
- /// Gets or sets the repeat mode.
- /// </summary>
- /// <value>The repeat mode.</value>
- public RepeatMode RepeatMode { get; set; }
- }
-
- public enum RepeatMode
- {
- RepeatNone = 0,
- RepeatAll = 1,
- RepeatOne = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlaybackStartInfo.cs b/MediaBrowser.Model/Session/PlaybackStartInfo.cs
deleted file mode 100644
index f6f496e4e..000000000
--- a/MediaBrowser.Model/Session/PlaybackStartInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class PlaybackStartInfo.
- /// </summary>
- public class PlaybackStartInfo : PlaybackProgressInfo
- {
- }
-}
diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs
deleted file mode 100644
index 160ef3554..000000000
--- a/MediaBrowser.Model/Session/PlaybackStopInfo.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class PlaybackStopInfo.
- /// </summary>
- public class PlaybackStopInfo
- {
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItemDto Item { get; set; }
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
- /// <summary>
- /// Gets or sets the session id.
- /// </summary>
- /// <value>The session id.</value>
- public string SessionId { get; set; }
- /// <summary>
- /// Gets or sets the media version identifier.
- /// </summary>
- /// <value>The media version identifier.</value>
- public string MediaSourceId { get; set; }
- /// <summary>
- /// Gets or sets the position ticks.
- /// </summary>
- /// <value>The position ticks.</value>
- public long? PositionTicks { get; set; }
- /// <summary>
- /// Gets or sets the live stream identifier.
- /// </summary>
- /// <value>The live stream identifier.</value>
- public string LiveStreamId { get; set; }
- /// <summary>
- /// Gets or sets the play session identifier.
- /// </summary>
- /// <value>The play session identifier.</value>
- public string PlaySessionId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="PlaybackStopInfo"/> is failed.
- /// </summary>
- /// <value><c>true</c> if failed; otherwise, <c>false</c>.</value>
- public bool Failed { get; set; }
-
- public string NextMediaType { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs
deleted file mode 100644
index f78842e29..000000000
--- a/MediaBrowser.Model/Session/PlayerStateInfo.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- public class PlayerStateInfo
- {
- /// <summary>
- /// Gets or sets the now playing position ticks.
- /// </summary>
- /// <value>The now playing position ticks.</value>
- public long? PositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can seek.
- /// </summary>
- /// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
- public bool CanSeek { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is paused.
- /// </summary>
- /// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value>
- public bool IsPaused { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is muted.
- /// </summary>
- /// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value>
- public bool IsMuted { get; set; }
-
- /// <summary>
- /// Gets or sets the volume level.
- /// </summary>
- /// <value>The volume level.</value>
- public int? VolumeLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the now playing audio stream.
- /// </summary>
- /// <value>The index of the now playing audio stream.</value>
- public int? AudioStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the now playing subtitle stream.
- /// </summary>
- /// <value>The index of the now playing subtitle stream.</value>
- public int? SubtitleStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the now playing media version identifier.
- /// </summary>
- /// <value>The now playing media version identifier.</value>
- public string MediaSourceId { get; set; }
-
- /// <summary>
- /// Gets or sets the play method.
- /// </summary>
- /// <value>The play method.</value>
- public PlayMethod? PlayMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the repeat mode.
- /// </summary>
- /// <value>The repeat mode.</value>
- public RepeatMode RepeatMode { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs
deleted file mode 100644
index 3b70d5454..000000000
--- a/MediaBrowser.Model/Session/PlaystateCommand.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Enum PlaystateCommand
- /// </summary>
- public enum PlaystateCommand
- {
- /// <summary>
- /// The stop
- /// </summary>
- Stop,
- /// <summary>
- /// The pause
- /// </summary>
- Pause,
- /// <summary>
- /// The unpause
- /// </summary>
- Unpause,
- /// <summary>
- /// The next track
- /// </summary>
- NextTrack,
- /// <summary>
- /// The previous track
- /// </summary>
- PreviousTrack,
- /// <summary>
- /// The seek
- /// </summary>
- Seek,
- /// <summary>
- /// The rewind
- /// </summary>
- Rewind,
- /// <summary>
- /// The fast forward
- /// </summary>
- FastForward,
- PlayPause
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/PlaystateRequest.cs b/MediaBrowser.Model/Session/PlaystateRequest.cs
deleted file mode 100644
index 8a046b503..000000000
--- a/MediaBrowser.Model/Session/PlaystateRequest.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- public class PlaystateRequest
- {
- public PlaystateCommand Command { get; set; }
-
- public long? SeekPositionTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the controlling user identifier.
- /// </summary>
- /// <value>The controlling user identifier.</value>
- public string ControllingUserId { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs
deleted file mode 100644
index 78ee72f61..000000000
--- a/MediaBrowser.Model/Session/SessionInfoDto.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-
-namespace MediaBrowser.Model.Session
-{
- [DebuggerDisplay("Client = {Client}, Username = {UserName}")]
- public class SessionInfoDto
- {
- /// <summary>
- /// Gets or sets the supported commands.
- /// </summary>
- /// <value>The supported commands.</value>
- public string[] SupportedCommands { get; set; }
-
- /// <summary>
- /// Gets or sets the playable media types.
- /// </summary>
- /// <value>The playable media types.</value>
- public string[] PlayableMediaTypes { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the user primary image tag.
- /// </summary>
- /// <value>The user primary image tag.</value>
- public string UserPrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the user.
- /// </summary>
- /// <value>The name of the user.</value>
- public string UserName { get; set; }
-
- /// <summary>
- /// Gets or sets the additional users present.
- /// </summary>
- /// <value>The additional users present.</value>
- public SessionUserInfo[] AdditionalUsers { get; set; }
-
- /// <summary>
- /// Gets or sets the application version.
- /// </summary>
- /// <value>The application version.</value>
- public string ApplicationVersion { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the client.
- /// </summary>
- /// <value>The type of the client.</value>
- public string Client { get; set; }
-
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime LastActivityDate { get; set; }
-
- /// <summary>
- /// Gets or sets the now viewing item.
- /// </summary>
- /// <value>The now viewing item.</value>
- public BaseItemDto NowViewingItem { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the device.
- /// </summary>
- /// <value>The name of the device.</value>
- public string DeviceName { get; set; }
-
- /// <summary>
- /// Gets or sets the now playing item.
- /// </summary>
- /// <value>The now playing item.</value>
- public BaseItemDto NowPlayingItem { get; set; }
-
- /// <summary>
- /// Gets or sets the device id.
- /// </summary>
- /// <value>The device id.</value>
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Gets or sets the application icon URL.
- /// </summary>
- /// <value>The application icon URL.</value>
- public string AppIconUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports remote control].
- /// </summary>
- /// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value>
- public bool SupportsRemoteControl { get; set; }
-
- public PlayerStateInfo PlayState { get; set; }
-
- public TranscodingInfo TranscodingInfo { get; set; }
-
- public SessionInfoDto()
- {
- AdditionalUsers = new SessionUserInfo[] { };
-
- PlayableMediaTypes = new string[] { };
- SupportedCommands = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Session/SessionUserInfo.cs b/MediaBrowser.Model/Session/SessionUserInfo.cs
deleted file mode 100644
index 39b96931a..000000000
--- a/MediaBrowser.Model/Session/SessionUserInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class SessionUserInfo.
- /// </summary>
- public class SessionUserInfo
- {
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the name of the user.
- /// </summary>
- /// <value>The name of the user.</value>
- public string UserName { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
deleted file mode 100644
index 70c299bc2..000000000
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Session
-{
- public class TranscodingInfo
- {
- public string AudioCodec { get; set; }
- public string VideoCodec { get; set; }
- public string Container { get; set; }
- public bool IsVideoDirect { get; set; }
- public bool IsAudioDirect { get; set; }
- public int? Bitrate { get; set; }
-
- public float? Framerate { get; set; }
- public double? CompletionPercentage { get; set; }
-
- public int? Width { get; set; }
- public int? Height { get; set; }
- public int? AudioChannels { get; set; }
-
- public TranscodeReason[] TranscodeReasons { get; set; }
-
- public TranscodingInfo()
- {
- TranscodeReasons = new TranscodeReason[] { };
- }
- }
-
- public enum TranscodeReason
- {
- ContainerNotSupported = 0,
- VideoCodecNotSupported = 1,
- AudioCodecNotSupported = 2,
- ContainerBitrateExceedsLimit = 3,
- AudioBitrateNotSupported = 4,
- AudioChannelsNotSupported = 5,
- VideoResolutionNotSupported = 6,
- UnknownVideoStreamInfo = 7,
- UnknownAudioStreamInfo = 8,
- AudioProfileNotSupported = 9,
- AudioSampleRateNotSupported = 10,
- AnamorphicVideoNotSupported = 11,
- InterlacedVideoNotSupported = 12,
- SecondaryAudioNotSupported = 13,
- RefFramesNotSupported = 14,
- VideoBitDepthNotSupported = 15,
- VideoBitrateNotSupported = 16,
- VideoFramerateNotSupported = 17,
- VideoLevelNotSupported = 18,
- VideoProfileNotSupported = 19,
- AudioBitDepthNotSupported = 20,
- SubtitleCodecNotSupported = 21
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Session/UserDataChangeInfo.cs b/MediaBrowser.Model/Session/UserDataChangeInfo.cs
deleted file mode 100644
index c6b03200d..000000000
--- a/MediaBrowser.Model/Session/UserDataChangeInfo.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Session
-{
- /// <summary>
- /// Class UserDataChangeInfo
- /// </summary>
- public class UserDataChangeInfo
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the user data list.
- /// </summary>
- /// <value>The user data list.</value>
- public UserItemDataDto[] UserDataList { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Social/ISharingManager.cs b/MediaBrowser.Model/Social/ISharingManager.cs
deleted file mode 100644
index 28c8c7db2..000000000
--- a/MediaBrowser.Model/Social/ISharingManager.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Social
-{
- public interface ISharingManager
- {
- /// <summary>
- /// Creates the share.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <returns>Task&lt;SocialShareInfo&gt;.</returns>
- Task<SocialShareInfo> CreateShare(string itemId, string userId);
- /// <summary>
- /// Gets the share information.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SocialShareInfo.</returns>
- SocialShareInfo GetShareInfo(string id);
- /// <summary>
- /// Deletes the share.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- void DeleteShare(string id);
- }
-}
diff --git a/MediaBrowser.Model/Social/ISharingRepository.cs b/MediaBrowser.Model/Social/ISharingRepository.cs
deleted file mode 100644
index dd88ddd04..000000000
--- a/MediaBrowser.Model/Social/ISharingRepository.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Social
-{
- public interface ISharingRepository
- {
- void CreateShare(SocialShareInfo info);
- void DeleteShare(string id);
- SocialShareInfo GetShareInfo(string id);
- }
-}
diff --git a/MediaBrowser.Model/Social/SocialShareInfo.cs b/MediaBrowser.Model/Social/SocialShareInfo.cs
deleted file mode 100644
index 1b1c225c4..000000000
--- a/MediaBrowser.Model/Social/SocialShareInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Social
-{
- public class SocialShareInfo
- {
- public string Id { get; set; }
- public string Url { get; set; }
- public string ItemId { get; set; }
- public string UserId { get; set; }
- public DateTime ExpirationDate { get; set; }
- public string Name { get; set; }
- public string ImageUrl { get; set; }
- public string Overview { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs b/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs
deleted file mode 100644
index adfb84b05..000000000
--- a/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class CompleteSyncJobInfo
- {
- public SyncJob Job { get; set; }
- public SyncJobItem[] JobItems { get; set; }
-
- public CompleteSyncJobInfo()
- {
- JobItems = new SyncJobItem[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/DeviceFileInfo.cs b/MediaBrowser.Model/Sync/DeviceFileInfo.cs
deleted file mode 100644
index bc93b69bc..000000000
--- a/MediaBrowser.Model/Sync/DeviceFileInfo.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class DeviceFileInfo
- {
- public string Path { get; set; }
- public string Name { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/ItemFIleInfo.cs b/MediaBrowser.Model/Sync/ItemFIleInfo.cs
deleted file mode 100644
index e023572fd..000000000
--- a/MediaBrowser.Model/Sync/ItemFIleInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Sync
-{
- public class ItemFileInfo
- {
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public ItemFileType Type { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
- /// <summary>
- /// Gets or sets the type of the image.
- /// </summary>
- /// <value>The type of the image.</value>
- public ImageType? ImageType { get; set; }
- /// <summary>
- /// Gets or sets the index.
- /// </summary>
- /// <value>The index.</value>
- public int Index { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/ItemFileType.cs b/MediaBrowser.Model/Sync/ItemFileType.cs
deleted file mode 100644
index 305f4c502..000000000
--- a/MediaBrowser.Model/Sync/ItemFileType.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public enum ItemFileType
- {
- /// <summary>
- /// The media
- /// </summary>
- Media = 0,
- /// <summary>
- /// The image
- /// </summary>
- Image = 1,
- /// <summary>
- /// The subtitles
- /// </summary>
- Subtitles = 2
- }
-}
diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs
deleted file mode 100644
index 3d625aa99..000000000
--- a/MediaBrowser.Model/Sync/LocalItem.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class LocalItem
- {
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItemDto Item { get; set; }
- /// <summary>
- /// Gets or sets the local path.
- /// </summary>
- /// <value>The local path.</value>
- public string LocalPath { get; set; }
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
- /// <summary>
- /// Gets or sets the unique identifier.
- /// </summary>
- /// <value>The unique identifier.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets the file identifier.
- /// </summary>
- /// <value>The file identifier.</value>
- public string FileId { get; set; }
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
- /// <summary>
- /// Gets or sets the synchronize job item identifier.
- /// </summary>
- /// <value>The synchronize job item identifier.</value>
- public string SyncJobItemId { get; set; }
- /// <summary>
- /// Gets or sets the user ids with access.
- /// </summary>
- /// <value>The user ids with access.</value>
- public string[] UserIdsWithAccess { get; set; }
- /// <summary>
- /// Gets or sets the additional files.
- /// </summary>
- /// <value>The additional files.</value>
- public string[] AdditionalFiles { get; set; }
-
- public LocalItem()
- {
- AdditionalFiles = new string[] { };
- UserIdsWithAccess = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/LocalItemInfo.cs b/MediaBrowser.Model/Sync/LocalItemInfo.cs
deleted file mode 100644
index f52873b2f..000000000
--- a/MediaBrowser.Model/Sync/LocalItemInfo.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class LocalItemInfo
- {
- public string ServerId { get; set; }
- public string Id { get; set; }
- public string Name { get; set; }
- public string PrimaryImageTag { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/LocalItemQuery.cs b/MediaBrowser.Model/Sync/LocalItemQuery.cs
deleted file mode 100644
index 795a557ca..000000000
--- a/MediaBrowser.Model/Sync/LocalItemQuery.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class LocalItemQuery
- {
- public string ServerId { get; set; }
- public string AlbumArtistId { get; set; }
- public string AlbumId { get; set; }
- public string SeriesId { get; set; }
- public string Type { get; set; }
- public string MediaType { get; set; }
- public string[] ExcludeTypes { get; set; }
-
- public LocalItemQuery()
- {
- ExcludeTypes = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncCategory.cs b/MediaBrowser.Model/Sync/SyncCategory.cs
deleted file mode 100644
index e0d748685..000000000
--- a/MediaBrowser.Model/Sync/SyncCategory.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public enum SyncCategory
- {
- /// <summary>
- /// The latest
- /// </summary>
- Latest = 0,
- /// <summary>
- /// The next up
- /// </summary>
- NextUp = 1,
- /// <summary>
- /// The resume
- /// </summary>
- Resume = 2
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncDataRequest.cs b/MediaBrowser.Model/Sync/SyncDataRequest.cs
deleted file mode 100644
index 79d1842e1..000000000
--- a/MediaBrowser.Model/Sync/SyncDataRequest.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncDataRequest
- {
- public string[] LocalItemIds { get; set; }
- public string[] SyncJobItemIds { get; set; }
-
- public string TargetId { get; set; }
-
- public SyncDataRequest()
- {
- LocalItemIds = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncDataResponse.cs b/MediaBrowser.Model/Sync/SyncDataResponse.cs
deleted file mode 100644
index 0b017af6e..000000000
--- a/MediaBrowser.Model/Sync/SyncDataResponse.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncDataResponse
- {
- public string[] ItemIdsToRemove { get; set; }
-
- public SyncDataResponse()
- {
- ItemIdsToRemove = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncDialogOptions.cs b/MediaBrowser.Model/Sync/SyncDialogOptions.cs
deleted file mode 100644
index e55ca4f08..000000000
--- a/MediaBrowser.Model/Sync/SyncDialogOptions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncDialogOptions
- {
- /// <summary>
- /// Gets or sets the targets.
- /// </summary>
- /// <value>The targets.</value>
- public SyncTarget[] Targets { get; set; }
- /// <summary>
- /// Gets or sets the options.
- /// </summary>
- /// <value>The options.</value>
- public SyncJobOption[] Options { get; set; }
- /// <summary>
- /// Gets or sets the quality options.
- /// </summary>
- /// <value>The quality options.</value>
- public SyncQualityOption[] QualityOptions { get; set; }
- /// <summary>
- /// Gets or sets the profile options.
- /// </summary>
- /// <value>The profile options.</value>
- public SyncProfileOption[] ProfileOptions { get; set; }
-
- public SyncDialogOptions()
- {
- Targets = new SyncTarget[] { };
- Options = new SyncJobOption[] { };
- QualityOptions = new SyncQualityOption[] { };
- ProfileOptions = new SyncProfileOption[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
deleted file mode 100644
index e8b698f62..000000000
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJob
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- public string TargetId { get; set; }
- /// <summary>
- /// Gets or sets the name of the target.
- /// </summary>
- /// <value>The name of the target.</value>
- public string TargetName { get; set; }
- /// <summary>
- /// Gets or sets the quality.
- /// </summary>
- /// <value>The quality.</value>
- public string Quality { get; set; }
- /// <summary>
- /// Gets or sets the bitrate.
- /// </summary>
- /// <value>The bitrate.</value>
- public int? Bitrate { get; set; }
- /// <summary>
- /// Gets or sets the profile.
- /// </summary>
- /// <value>The profile.</value>
- public string Profile { get; set; }
- /// <summary>
- /// Gets or sets the category.
- /// </summary>
- /// <value>The category.</value>
- public SyncCategory? Category { get; set; }
- /// <summary>
- /// Gets or sets the parent identifier.
- /// </summary>
- /// <value>The parent identifier.</value>
- public string ParentId { get; set; }
- /// <summary>
- /// Gets or sets the current progress.
- /// </summary>
- /// <value>The current progress.</value>
- public double? Progress { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public SyncJobStatus Status { get; set; }
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [unwatched only].
- /// </summary>
- /// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value>
- public bool UnwatchedOnly { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [synchronize new content].
- /// </summary>
- /// <value><c>true</c> if [synchronize new content]; otherwise, <c>false</c>.</value>
- public bool SyncNewContent { get; set; }
- /// <summary>
- /// Gets or sets the item limit.
- /// </summary>
- /// <value>The item limit.</value>
- public int? ItemLimit { get; set; }
- /// <summary>
- /// Gets or sets the requested item ids.
- /// </summary>
- /// <value>The requested item ids.</value>
- public string[] RequestedItemIds { get; set; }
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- public DateTime DateCreated { get; set; }
- /// <summary>
- /// Gets or sets the date last modified.
- /// </summary>
- /// <value>The date last modified.</value>
- public DateTime DateLastModified { get; set; }
- /// <summary>
- /// Gets or sets the item count.
- /// </summary>
- /// <value>The item count.</value>
- public int ItemCount { get; set; }
-
- public string ParentName { get; set; }
- public string PrimaryImageItemId { get; set; }
- public string PrimaryImageTag { get; set; }
-
- public SyncJob()
- {
- RequestedItemIds = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobCreationResult.cs b/MediaBrowser.Model/Sync/SyncJobCreationResult.cs
deleted file mode 100644
index ee46bc155..000000000
--- a/MediaBrowser.Model/Sync/SyncJobCreationResult.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJobCreationResult
- {
- public SyncJob Job { get; set; }
- public SyncJobItem[] JobItems { get; set; }
-
- public SyncJobCreationResult()
- {
- JobItems = new SyncJobItem[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs
deleted file mode 100644
index 5a97bc92e..000000000
--- a/MediaBrowser.Model/Sync/SyncJobItem.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJobItem
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the job identifier.
- /// </summary>
- /// <value>The job identifier.</value>
- public string JobId { get; set; }
-
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the item.
- /// </summary>
- /// <value>The name of the item.</value>
- public string ItemName { get; set; }
-
- /// <summary>
- /// Gets or sets the media source identifier.
- /// </summary>
- /// <value>The media source identifier.</value>
- public string MediaSourceId { get; set; }
-
- /// <summary>
- /// Gets or sets the media source.
- /// </summary>
- /// <value>The media source.</value>
- public MediaSourceInfo MediaSource { get; set; }
-
- /// <summary>
- /// Gets or sets the target identifier.
- /// </summary>
- /// <value>The target identifier.</value>
- public string TargetId { get; set; }
-
- /// <summary>
- /// Gets or sets the output path.
- /// </summary>
- /// <value>The output path.</value>
- public string OutputPath { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public SyncJobItemStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the current progress.
- /// </summary>
- /// <value>The current progress.</value>
- public double? Progress { get; set; }
-
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- public DateTime DateCreated { get; set; }
- /// <summary>
- /// Gets or sets the primary image item identifier.
- /// </summary>
- /// <value>The primary image item identifier.</value>
- public string PrimaryImageItemId { get; set; }
- /// <summary>
- /// Gets or sets the primary image tag.
- /// </summary>
- /// <value>The primary image tag.</value>
- public string PrimaryImageTag { get; set; }
- /// <summary>
- /// Gets or sets the temporary path.
- /// </summary>
- /// <value>The temporary path.</value>
- public string TemporaryPath { get; set; }
- /// <summary>
- /// Gets or sets the additional files.
- /// </summary>
- /// <value>The additional files.</value>
- public ItemFileInfo[] AdditionalFiles { get; set; }
- /// <summary>
- /// Gets or sets the index of the job item.
- /// </summary>
- /// <value>The index of the job item.</value>
- public int JobItemIndex { get; set; }
-
- public long ItemDateModifiedTicks { get; set; }
-
- public SyncJobItem()
- {
- AdditionalFiles = new ItemFileInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobItemQuery.cs b/MediaBrowser.Model/Sync/SyncJobItemQuery.cs
deleted file mode 100644
index 74d3ac096..000000000
--- a/MediaBrowser.Model/Sync/SyncJobItemQuery.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJobItemQuery
- {
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- /// <summary>
- /// Gets or sets the job identifier.
- /// </summary>
- /// <value>The job identifier.</value>
- public string JobId { get; set; }
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
- /// <summary>
- /// Gets or sets the target identifier.
- /// </summary>
- /// <value>The target identifier.</value>
- public string TargetId { get; set; }
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public SyncJobItemStatus[] Statuses { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [add metadata].
- /// </summary>
- /// <value><c>true</c> if [add metadata]; otherwise, <c>false</c>.</value>
- public bool AddMetadata { get; set; }
-
- public SyncJobItemQuery()
- {
- Statuses = new SyncJobItemStatus[] {};
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs
deleted file mode 100644
index 2a968869f..000000000
--- a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public enum SyncJobItemStatus
- {
- Queued = 0,
- Converting = 1,
- ReadyToTransfer = 2,
- Transferring = 3,
- Synced = 4,
- Failed = 5
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobQuery.cs b/MediaBrowser.Model/Sync/SyncJobQuery.cs
deleted file mode 100644
index ed9e5ae60..000000000
--- a/MediaBrowser.Model/Sync/SyncJobQuery.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJobQuery
- {
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- /// <summary>
- /// Gets or sets the target identifier.
- /// </summary>
- /// <value>The target identifier.</value>
- public string TargetId { get; set; }
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- public string ExcludeTargetIds { get; set; }
- public string ItemId { get; set; }
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public SyncJobStatus[] Statuses { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [synchronize new content].
- /// </summary>
- /// <value><c>null</c> if [synchronize new content] contains no value, <c>true</c> if [synchronize new content]; otherwise, <c>false</c>.</value>
- public bool? SyncNewContent { get; set; }
-
- public SyncJobQuery()
- {
- Statuses = new SyncJobStatus[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobRequest.cs b/MediaBrowser.Model/Sync/SyncJobRequest.cs
deleted file mode 100644
index 3dc863b75..000000000
--- a/MediaBrowser.Model/Sync/SyncJobRequest.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncJobRequest
- {
- /// <summary>
- /// Gets or sets the target identifier.
- /// </summary>
- /// <value>The target identifier.</value>
- public string TargetId { get; set; }
- /// <summary>
- /// Gets or sets the item ids.
- /// </summary>
- /// <value>The item ids.</value>
- public string[] ItemIds { get; set; }
- /// <summary>
- /// Gets or sets the category.
- /// </summary>
- /// <value>The category.</value>
- public SyncCategory? Category { get; set; }
- /// <summary>
- /// Gets or sets the parent identifier.
- /// </summary>
- /// <value>The parent identifier.</value>
- public string ParentId { get; set; }
- /// <summary>
- /// Gets or sets the quality.
- /// </summary>
- /// <value>The quality.</value>
- public string Quality { get; set; }
- /// <summary>
- /// Gets or sets the profile.
- /// </summary>
- /// <value>The profile.</value>
- public string Profile { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [unwatched only].
- /// </summary>
- /// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value>
- public bool UnwatchedOnly { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [synchronize new content].
- /// </summary>
- /// <value><c>true</c> if [synchronize new content]; otherwise, <c>false</c>.</value>
- public bool SyncNewContent { get; set; }
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? ItemLimit { get; set; }
- /// <summary>
- /// Gets or sets the bitrate.
- /// </summary>
- /// <value>The bitrate.</value>
- public int? Bitrate { get; set; }
-
- public SyncJobRequest()
- {
- ItemIds = new string[] { };
- SyncNewContent = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs
deleted file mode 100644
index 2d1d30802..000000000
--- a/MediaBrowser.Model/Sync/SyncJobStatus.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public enum SyncJobStatus
- {
- Queued = 0,
- Converting = 1,
- ReadyToTransfer = 2,
- Transferring = 3,
- Completed = 4,
- CompletedWithError = 5,
- Failed = 6
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncOptions.cs b/MediaBrowser.Model/Sync/SyncOptions.cs
deleted file mode 100644
index 7f0c17b37..000000000
--- a/MediaBrowser.Model/Sync/SyncOptions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncOptions
- {
- public string TemporaryPath { get; set; }
- public long UploadSpeedLimitBytes { get; set; }
- public int TranscodingCpuCoreLimit { get; set; }
- public bool EnableFullSpeedTranscoding { get; set; }
-
- public SyncOptions()
- {
- TranscodingCpuCoreLimit = 1;
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncParameter.cs b/MediaBrowser.Model/Sync/SyncParameter.cs
deleted file mode 100644
index bce2a4f30..000000000
--- a/MediaBrowser.Model/Sync/SyncParameter.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public enum SyncJobOption
- {
- Name = 0,
- Quality = 1,
- UnwatchedOnly = 2,
- SyncNewContent = 3,
- ItemLimit = 4,
- Profile = 5
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncProfileOption.cs b/MediaBrowser.Model/Sync/SyncProfileOption.cs
deleted file mode 100644
index 605af6b25..000000000
--- a/MediaBrowser.Model/Sync/SyncProfileOption.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncProfileOption
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description { get; set; }
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is default.
- /// </summary>
- /// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
- public bool IsDefault { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether [enable quality options].
- /// </summary>
- /// <value><c>true</c> if [enable quality options]; otherwise, <c>false</c>.</value>
- public bool EnableQualityOptions { get; set; }
-
- public SyncProfileOption()
- {
- EnableQualityOptions = true;
- }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncQualityOption.cs b/MediaBrowser.Model/Sync/SyncQualityOption.cs
deleted file mode 100644
index 6eff4b9a4..000000000
--- a/MediaBrowser.Model/Sync/SyncQualityOption.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncQualityOption
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description { get; set; }
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is default.
- /// </summary>
- /// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
- public bool IsDefault { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is original quality.
- /// </summary>
- /// <value><c>true</c> if this instance is original quality; otherwise, <c>false</c>.</value>
- public bool IsOriginalQuality { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncTarget.cs b/MediaBrowser.Model/Sync/SyncTarget.cs
deleted file mode 100644
index 8901f0f27..000000000
--- a/MediaBrowser.Model/Sync/SyncTarget.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncTarget
- {
- /// <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; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncedItem.cs b/MediaBrowser.Model/Sync/SyncedItem.cs
deleted file mode 100644
index 68bd8a2eb..000000000
--- a/MediaBrowser.Model/Sync/SyncedItem.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncedItem
- {
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
- /// <summary>
- /// Gets or sets the synchronize job identifier.
- /// </summary>
- /// <value>The synchronize job identifier.</value>
- public string SyncJobId { get; set; }
- /// <summary>
- /// Gets or sets the name of the synchronize job.
- /// </summary>
- /// <value>The name of the synchronize job.</value>
- public string SyncJobName { get; set; }
- /// <summary>
- /// Gets or sets the synchronize job date created.
- /// </summary>
- /// <value>The synchronize job date created.</value>
- public DateTime SyncJobDateCreated { get; set; }
- /// <summary>
- /// Gets or sets the synchronize job item identifier.
- /// </summary>
- /// <value>The synchronize job item identifier.</value>
- public string SyncJobItemId { 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 item.
- /// </summary>
- /// <value>The item.</value>
- public BaseItemDto Item { get; set; }
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
- /// <summary>
- /// Gets or sets the additional files.
- /// </summary>
- /// <value>The additional files.</value>
- public ItemFileInfo[] AdditionalFiles { get; set; }
-
- public SyncedItem()
- {
- AdditionalFiles = new ItemFileInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/System/Architecture.cs b/MediaBrowser.Model/System/Architecture.cs
deleted file mode 100644
index 73f78cd58..000000000
--- a/MediaBrowser.Model/System/Architecture.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace MediaBrowser.Model.System
-{
- public enum Architecture
- {
- X86 = 0,
- X64 = 1,
- Arm = 2,
- Arm64 = 3
- }
-}
diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs
deleted file mode 100644
index 4430bfe07..000000000
--- a/MediaBrowser.Model/System/IEnvironmentInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-namespace MediaBrowser.Model.System
-{
- public interface IEnvironmentInfo
- {
- MediaBrowser.Model.System.OperatingSystem OperatingSystem { get; }
- string OperatingSystemName { get; }
- string OperatingSystemVersion { get; }
- Architecture SystemArchitecture { get; }
- string GetEnvironmentVariable(string name);
- void SetProcessEnvironmentVariable(string name, string value);
- string GetUserId();
- string StackTrace { get; }
- char PathSeparator { get; }
- }
-
- public enum OperatingSystem
- {
- Windows,
- Linux,
- OSX,
- BSD
- }
-}
diff --git a/MediaBrowser.Model/System/IPowerManagement.cs b/MediaBrowser.Model/System/IPowerManagement.cs
deleted file mode 100644
index 91cae0d3e..000000000
--- a/MediaBrowser.Model/System/IPowerManagement.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-
-namespace MediaBrowser.Model.System
-{
- public interface IPowerManagement
- {
- void PreventSystemStandby();
- void AllowSystemStandby();
- }
-}
diff --git a/MediaBrowser.Model/System/ISystemEvents.cs b/MediaBrowser.Model/System/ISystemEvents.cs
deleted file mode 100644
index dec8ed8c0..000000000
--- a/MediaBrowser.Model/System/ISystemEvents.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.System
-{
- public interface ISystemEvents
- {
- event EventHandler Resume;
- event EventHandler Suspend;
- event EventHandler SessionLogoff;
- event EventHandler SystemShutdown;
- }
-}
diff --git a/MediaBrowser.Model/System/LogFile.cs b/MediaBrowser.Model/System/LogFile.cs
deleted file mode 100644
index ba409c542..000000000
--- a/MediaBrowser.Model/System/LogFile.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.System
-{
- public class LogFile
- {
- /// <summary>
- /// Gets or sets the date created.
- /// </summary>
- /// <value>The date created.</value>
- public DateTime DateCreated { get; set; }
-
- /// <summary>
- /// Gets or sets the date modified.
- /// </summary>
- /// <value>The date modified.</value>
- public DateTime DateModified { get; set; }
-
- /// <summary>
- /// Gets or sets the size.
- /// </summary>
- /// <value>The size.</value>
- public long Size { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
deleted file mode 100644
index b9a3260b0..000000000
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-namespace MediaBrowser.Model.System
-{
- public class PublicSystemInfo
- {
- /// <summary>
- /// Gets or sets the local address.
- /// </summary>
- /// <value>The local address.</value>
- public string LocalAddress { get; set; }
-
- /// <summary>
- /// Gets or sets the wan address.
- /// </summary>
- /// <value>The wan address.</value>
- public string WanAddress { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the server.
- /// </summary>
- /// <value>The name of the server.</value>
- public string ServerName { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets the operating sytem.
- /// </summary>
- /// <value>The operating sytem.</value>
- public string OperatingSystem { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
deleted file mode 100644
index 9ed0f904f..000000000
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-using MediaBrowser.Model.Updates;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.System
-{
- /// <summary>
- /// Class SystemInfo
- /// </summary>
- public class SystemInfo : PublicSystemInfo
- {
- public PackageVersionClass SystemUpdateLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the display name of the operating system.
- /// </summary>
- /// <value>The display name of the operating system.</value>
- public string OperatingSystemDisplayName { get; set; }
-
- /// <summary>
- /// Gets or sets the mac address.
- /// </summary>
- /// <value>The mac address.</value>
- public string MacAddress { get; set; }
-
- public string PackageName { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has pending restart.
- /// </summary>
- /// <value><c>true</c> if this instance has pending restart; otherwise, <c>false</c>.</value>
- public bool HasPendingRestart { get; set; }
-
- public bool IsShuttingDown { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports library monitor].
- /// </summary>
- /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value>
- public bool SupportsLibraryMonitor { get; set; }
-
- /// <summary>
- /// Gets or sets the in progress installations.
- /// </summary>
- /// <value>The in progress installations.</value>
- public InstallationInfo[] InProgressInstallations { get; set; }
-
- /// <summary>
- /// Gets or sets the web socket port number.
- /// </summary>
- /// <value>The web socket port number.</value>
- public int WebSocketPortNumber { get; set; }
-
- /// <summary>
- /// Gets or sets the completed installations.
- /// </summary>
- /// <value>The completed installations.</value>
- public InstallationInfo[] CompletedInstallations { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self restart.
- /// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public bool CanSelfRestart { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public bool CanSelfUpdate { get; set; }
-
- public bool CanLaunchWebBrowser { get; set; }
-
- /// <summary>
- /// Gets or sets plugin assemblies that failed to load.
- /// </summary>
- /// <value>The failed assembly loads.</value>
- public string[] FailedPluginAssemblies { get; set; }
-
- /// <summary>
- /// Gets or sets the program data path.
- /// </summary>
- /// <value>The program data path.</value>
- public string ProgramDataPath { get; set; }
-
- /// <summary>
- /// Gets or sets the items by name path.
- /// </summary>
- /// <value>The items by name path.</value>
- public string ItemsByNamePath { get; set; }
-
- /// <summary>
- /// Gets or sets the cache path.
- /// </summary>
- /// <value>The cache path.</value>
- public string CachePath { get; set; }
-
- /// <summary>
- /// Gets or sets the log path.
- /// </summary>
- /// <value>The log path.</value>
- public string LogPath { get; set; }
-
- /// <summary>
- /// Gets or sets the internal metadata path.
- /// </summary>
- /// <value>The internal metadata path.</value>
- public string InternalMetadataPath { get; set; }
-
- /// <summary>
- /// Gets or sets the transcoding temporary path.
- /// </summary>
- /// <value>The transcoding temporary path.</value>
- public string TranscodingTempPath { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTP server port number.
- /// </summary>
- /// <value>The HTTP server port number.</value>
- public int HttpServerPortNumber { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable HTTPS].
- /// </summary>
- /// <value><c>true</c> if [enable HTTPS]; otherwise, <c>false</c>.</value>
- public bool SupportsHttps { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTPS server port number.
- /// </summary>
- /// <value>The HTTPS server port number.</value>
- public int HttpsPortNumber { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- public bool HasUpdateAvailable { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [supports automatic run at startup].
- /// </summary>
- /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value>
- public bool SupportsAutoRunAtStartup { get; set; }
-
- public string EncoderLocationType { get; set; }
-
- public Architecture SystemArchitecture { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SystemInfo" /> class.
- /// </summary>
- public SystemInfo()
- {
- InProgressInstallations = new InstallationInfo[] { };
-
- CompletedInstallations = new InstallationInfo[] { };
-
- FailedPluginAssemblies = new string[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
deleted file mode 100644
index ed981a905..000000000
--- a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace MediaBrowser.Model.Tasks
-{
- public interface IConfigurableScheduledTask
- {
- /// <summary>
- /// Gets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- bool IsHidden { get; }
- /// <summary>
- /// Gets a value indicating whether this instance is enabled.
- /// </summary>
- /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
- bool IsEnabled { get; }
-
- bool IsLogged { get; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Tasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs
deleted file mode 100644
index 81ba239ad..000000000
--- a/MediaBrowser.Model/Tasks/IScheduledTask.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Interface IScheduledTaskWorker
- /// </summary>
- public interface IScheduledTask
- {
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- string Key { get; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- string Description { get; }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- string Category { get; }
-
- /// <summary>
- /// Executes the task
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- Task Execute(CancellationToken cancellationToken, IProgress<double> progress);
-
- /// <summary>
- /// Gets the default triggers.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- IEnumerable<TaskTriggerInfo> GetDefaultTriggers();
- }
-}
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
deleted file mode 100644
index 415207f8f..000000000
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using MediaBrowser.Model.Events;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Interface IScheduledTaskWorker
- /// </summary>
- public interface IScheduledTaskWorker : IDisposable
- {
- /// <summary>
- /// Occurs when [task progress].
- /// </summary>
- event EventHandler<GenericEventArgs<double>> TaskProgress;
-
- /// <summary>
- /// Gets or sets the scheduled task.
- /// </summary>
- /// <value>The scheduled task.</value>
- IScheduledTask ScheduledTask { get; }
-
- /// <summary>
- /// Gets the last execution result.
- /// </summary>
- /// <value>The last execution result.</value>
- TaskResult LastExecutionResult { get; }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- string Description { get; }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- string Category { get; }
-
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
- TaskState State { get; }
-
- /// <summary>
- /// Gets the current progress.
- /// </summary>
- /// <value>The current progress.</value>
- double? CurrentProgress { get; }
-
- /// <summary>
- /// Gets the triggers that define when the task will run
- /// </summary>
- /// <value>The triggers.</value>
- /// <exception cref="ArgumentNullException">value</exception>
- TaskTriggerInfo[] Triggers { get; set; }
-
- /// <summary>
- /// Gets the unique id.
- /// </summary>
- /// <value>The unique id.</value>
- string Id { get; }
-
- /// <summary>
- /// Reloads the trigger events.
- /// </summary>
- void ReloadTriggerEvents();
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
deleted file mode 100644
index 1674fc107..000000000
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
-
-namespace MediaBrowser.Model.Tasks
-{
- public interface ITaskManager : IDisposable
- {
- /// <summary>
- /// Gets the list of Scheduled Tasks
- /// </summary>
- /// <value>The scheduled tasks.</value>
- IScheduledTaskWorker[] ScheduledTasks { get; }
-
- /// <summary>
- /// Cancels if running and queue.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="options">Task options.</param>
- void CancelIfRunningAndQueue<T>(TaskExecutionOptions options)
- where T : IScheduledTask;
-
- /// <summary>
- /// Cancels if running and queue.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- void CancelIfRunningAndQueue<T>()
- where T : IScheduledTask;
-
- /// <summary>
- /// Cancels if running.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- void CancelIfRunning<T>()
- where T : IScheduledTask;
-
- /// <summary>
- /// Queues the scheduled task.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="options">Task options.</param>
- void QueueScheduledTask<T>(TaskExecutionOptions options)
- where T : IScheduledTask;
-
- /// <summary>
- /// Queues the scheduled task.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- void QueueScheduledTask<T>()
- where T : IScheduledTask;
-
- void QueueIfNotRunning<T>()
- where T : IScheduledTask;
-
- /// <summary>
- /// Queues the scheduled task.
- /// </summary>
- /// <param name="task">The task.</param>
- /// <param name="options">The task run options.</param>
- void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options = null);
-
- /// <summary>
- /// Adds the tasks.
- /// </summary>
- /// <param name="tasks">The tasks.</param>
- void AddTasks(IEnumerable<IScheduledTask> tasks);
-
- void Cancel(IScheduledTaskWorker task);
- Task Execute(IScheduledTaskWorker task, TaskExecutionOptions options = null);
-
- void Execute<T>()
- where T : IScheduledTask;
-
- event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
- event EventHandler<TaskCompletionEventArgs> TaskCompleted;
-
- void RunTaskOnNextStartup(string key);
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
deleted file mode 100644
index 3beca569c..000000000
--- a/MediaBrowser.Model/Tasks/ITaskTrigger.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Interface ITaskTrigger
- /// </summary>
- public interface ITaskTrigger
- {
- /// <summary>
- /// Fires when the trigger condition is satisfied and the task should run
- /// </summary>
- event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
-
- /// <summary>
- /// Stars waiting for the trigger action
- /// </summary>
- void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup);
-
- /// <summary>
- /// Stops waiting for the trigger action
- /// </summary>
- void Stop();
-
- /// <summary>
- /// Gets or sets the execution properties of this task.
- /// </summary>
- /// <value>
- /// The execution properties of this task.
- /// </value>
- TaskExecutionOptions TaskOptions { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
deleted file mode 100644
index 2dec79e93..000000000
--- a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Class ScheduledTaskHelpers
- /// </summary>
- public static class ScheduledTaskHelpers
- {
- /// <summary>
- /// Gets the task info.
- /// </summary>
- /// <param name="task">The task.</param>
- /// <returns>TaskInfo.</returns>
- public static TaskInfo GetTaskInfo(IScheduledTaskWorker task)
- {
- var isHidden = false;
-
- var configurableTask = task.ScheduledTask as IConfigurableScheduledTask;
-
- if (configurableTask != null)
- {
- isHidden = configurableTask.IsHidden;
- }
-
- string key = task.ScheduledTask.Key;
-
- return new TaskInfo
- {
- Name = task.Name,
- CurrentProgressPercentage = task.CurrentProgress,
- State = task.State,
- Id = task.Id,
- LastExecutionResult = task.LastExecutionResult,
-
- Triggers = task.Triggers,
-
- Description = task.Description,
- Category = task.Category,
- IsHidden = isHidden,
- Key = key
- };
- }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/SystemEvent.cs b/MediaBrowser.Model/Tasks/SystemEvent.cs
deleted file mode 100644
index 4d49a38cc..000000000
--- a/MediaBrowser.Model/Tasks/SystemEvent.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Enum SystemEvent
- /// </summary>
- public enum SystemEvent
- {
- /// <summary>
- /// The wake from sleep
- /// </summary>
- WakeFromSleep = 0
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
deleted file mode 100644
index be9eaa613..000000000
--- a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Tasks
-{
- public class TaskCompletionEventArgs : EventArgs
- {
- public IScheduledTaskWorker Task { get; set; }
-
- public TaskResult Result { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs b/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs
deleted file mode 100644
index 6ba5ba5e4..000000000
--- a/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Enum TaskCompletionStatus
- /// </summary>
- public enum TaskCompletionStatus
- {
- /// <summary>
- /// The completed
- /// </summary>
- Completed,
-
- /// <summary>
- /// The failed
- /// </summary>
- Failed,
-
- /// <summary>
- /// Manually cancelled by the user
- /// </summary>
- Cancelled,
-
- /// <summary>
- /// Aborted due to a system failure or shutdown
- /// </summary>
- Aborted
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskExecutionOptions.cs b/MediaBrowser.Model/Tasks/TaskExecutionOptions.cs
deleted file mode 100644
index faba35b22..000000000
--- a/MediaBrowser.Model/Tasks/TaskExecutionOptions.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// A class that encomposases all common task run properties.
- /// </summary>
- public class TaskExecutionOptions
- {
- public int? MaxRuntimeMs { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs
deleted file mode 100644
index 8792ce952..000000000
--- a/MediaBrowser.Model/Tasks/TaskInfo.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Class TaskInfo
- /// </summary>
- public class TaskInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the state of the task.
- /// </summary>
- /// <value>The state of the task.</value>
- public TaskState State { get; set; }
-
- /// <summary>
- /// Gets or sets the progress.
- /// </summary>
- /// <value>The progress.</value>
- public double? CurrentProgressPercentage { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the last execution result.
- /// </summary>
- /// <value>The last execution result.</value>
- public TaskResult LastExecutionResult { get; set; }
-
- /// <summary>
- /// Gets or sets the triggers.
- /// </summary>
- /// <value>The triggers.</value>
- public TaskTriggerInfo[] Triggers { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description { get; set; }
-
- /// <summary>
- /// Gets or sets the category.
- /// </summary>
- /// <value>The category.</value>
- public string Category { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- public bool IsHidden { get; set; }
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TaskInfo"/> class.
- /// </summary>
- public TaskInfo()
- {
- Triggers = new TaskTriggerInfo[]{};
- }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskResult.cs b/MediaBrowser.Model/Tasks/TaskResult.cs
deleted file mode 100644
index 39eacdf66..000000000
--- a/MediaBrowser.Model/Tasks/TaskResult.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Class TaskExecutionInfo
- /// </summary>
- public class TaskResult
- {
- /// <summary>
- /// Gets or sets the start time UTC.
- /// </summary>
- /// <value>The start time UTC.</value>
- public DateTime StartTimeUtc { get; set; }
-
- /// <summary>
- /// Gets or sets the end time UTC.
- /// </summary>
- /// <value>The end time UTC.</value>
- public DateTime EndTimeUtc { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public TaskCompletionStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- /// <value>The key.</value>
- public string Key { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the error message.
- /// </summary>
- /// <value>The error message.</value>
- public string ErrorMessage { get; set; }
-
- /// <summary>
- /// Gets or sets the long error message.
- /// </summary>
- /// <value>The long error message.</value>
- public string LongErrorMessage { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskState.cs b/MediaBrowser.Model/Tasks/TaskState.cs
deleted file mode 100644
index 889ce6875..000000000
--- a/MediaBrowser.Model/Tasks/TaskState.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Enum TaskState
- /// </summary>
- public enum TaskState
- {
- /// <summary>
- /// The idle
- /// </summary>
- Idle,
- /// <summary>
- /// The cancelling
- /// </summary>
- Cancelling,
- /// <summary>
- /// The running
- /// </summary>
- Running
- }
-}
diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
deleted file mode 100644
index 69578c41d..000000000
--- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Tasks
-{
- /// <summary>
- /// Class TaskTriggerInfo
- /// </summary>
- public class TaskTriggerInfo
- {
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the time of day.
- /// </summary>
- /// <value>The time of day.</value>
- public long? TimeOfDayTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the interval.
- /// </summary>
- /// <value>The interval.</value>
- public long? IntervalTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the system event.
- /// </summary>
- /// <value>The system event.</value>
- public SystemEvent? SystemEvent { get; set; }
-
- /// <summary>
- /// Gets or sets the day of week.
- /// </summary>
- /// <value>The day of week.</value>
- public DayOfWeek? DayOfWeek { get; set; }
-
- /// <summary>
- /// Gets or sets the maximum runtime ms.
- /// </summary>
- /// <value>The maximum runtime ms.</value>
- public int? MaxRuntimeMs { get; set; }
-
- public const string TriggerDaily = "DailyTrigger";
- public const string TriggerWeekly = "WeeklyTrigger";
- public const string TriggerInterval = "IntervalTrigger";
- public const string TriggerSystemEvent = "SystemEventTrigger";
- public const string TriggerStartup = "StartupTrigger";
- }
-}
diff --git a/MediaBrowser.Model/Text/ITextEncoding.cs b/MediaBrowser.Model/Text/ITextEncoding.cs
deleted file mode 100644
index 619d90a2b..000000000
--- a/MediaBrowser.Model/Text/ITextEncoding.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.IO;
-using System.Text;
-
-namespace MediaBrowser.Model.Text
-{
- public interface ITextEncoding
- {
- Encoding GetASCIIEncoding();
-
- string GetDetectedEncodingName(byte[] bytes, int size, string language, bool enableLanguageDetection);
- Encoding GetDetectedEncoding(byte[] bytes, int size, string language, bool enableLanguageDetection);
- Encoding GetEncodingFromCharset(string charset);
- }
-}
diff --git a/MediaBrowser.Model/Threading/ITimer.cs b/MediaBrowser.Model/Threading/ITimer.cs
deleted file mode 100644
index 42090250b..000000000
--- a/MediaBrowser.Model/Threading/ITimer.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Threading
-{
- public interface ITimer : IDisposable
- {
- void Change(TimeSpan dueTime, TimeSpan period);
- void Change(int dueTimeMs, int periodMs);
- }
-}
diff --git a/MediaBrowser.Model/Threading/ITimerFactory.cs b/MediaBrowser.Model/Threading/ITimerFactory.cs
deleted file mode 100644
index 5f3df1738..000000000
--- a/MediaBrowser.Model/Threading/ITimerFactory.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Threading
-{
- public interface ITimerFactory
- {
- ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period);
- ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs);
- }
-}
diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
deleted file mode 100644
index ff0bba197..000000000
--- a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Class CheckForUpdateResult
- /// </summary>
- public class CheckForUpdateResult
- {
- /// <summary>
- /// Gets or sets a value indicating whether this instance is update available.
- /// </summary>
- /// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value>
- public bool IsUpdateAvailable { get; set; }
-
- /// <summary>
- /// Gets or sets the available version.
- /// </summary>
- /// <value>The available version.</value>
- public string AvailableVersion
- {
- get { return Package != null ? Package.versionStr : "0.0.0.1"; }
- set { } // need this for the serializer
- }
-
- /// <summary>
- /// Get or sets package information for an available update
- /// </summary>
- public PackageVersionInfo Package { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs
deleted file mode 100644
index 8c6e686d8..000000000
--- a/MediaBrowser.Model/Updates/InstallationInfo.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Class InstallationInfo
- /// </summary>
- public class InstallationInfo
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the assembly guid.
- /// </summary>
- /// <value>The guid of the assembly.</value>
- public string AssemblyGuid { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets the update class.
- /// </summary>
- /// <value>The update class.</value>
- public PackageVersionClass UpdateClass { get; set; }
-
- /// <summary>
- /// Gets or sets the percent complete.
- /// </summary>
- /// <value>The percent complete.</value>
- public double? PercentComplete { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs
deleted file mode 100644
index e46d59fc0..000000000
--- a/MediaBrowser.Model/Updates/PackageInfo.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Class PackageInfo
- /// </summary>
- public class PackageInfo
- {
- /// <summary>
- /// The internal id of this package.
- /// </summary>
- /// <value>The id.</value>
- public string id { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string name { get; set; }
-
- /// <summary>
- /// Gets or sets the short description.
- /// </summary>
- /// <value>The short description.</value>
- public string shortDescription { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string overview { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premium.
- /// </summary>
- /// <value><c>true</c> if this instance is premium; otherwise, <c>false</c>.</value>
- public bool isPremium { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is adult only content.
- /// </summary>
- /// <value><c>true</c> if this instance is adult; otherwise, <c>false</c>.</value>
- public bool adult { get; set; }
-
- /// <summary>
- /// Gets or sets the rich desc URL.
- /// </summary>
- /// <value>The rich desc URL.</value>
- public string richDescUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the thumb image.
- /// </summary>
- /// <value>The thumb image.</value>
- public string thumbImage { get; set; }
-
- /// <summary>
- /// Gets or sets the preview image.
- /// </summary>
- /// <value>The preview image.</value>
- public string previewImage { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string type { get; set; }
-
- /// <summary>
- /// Gets or sets the target filename.
- /// </summary>
- /// <value>The target filename.</value>
- public string targetFilename { get; set; }
-
- /// <summary>
- /// Gets or sets the owner.
- /// </summary>
- /// <value>The owner.</value>
- public string owner { get; set; }
-
- /// <summary>
- /// Gets or sets the category.
- /// </summary>
- /// <value>The category.</value>
- public string category { get; set; }
-
- /// <summary>
- /// Gets or sets the catalog tile color.
- /// </summary>
- /// <value>The owner.</value>
- public string tileColor { get; set; }
-
- /// <summary>
- /// Gets or sets the feature id of this package (if premium).
- /// </summary>
- /// <value>The feature id.</value>
- public string featureId { get; set; }
-
- /// <summary>
- /// Gets or sets the registration info for this package (if premium).
- /// </summary>
- /// <value>The registration info.</value>
- public string regInfo { get; set; }
-
- /// <summary>
- /// Gets or sets the price for this package (if premium).
- /// </summary>
- /// <value>The price.</value>
- public float price { get; set; }
-
- /// <summary>
- /// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic).
- /// </summary>
- /// <value>The target system.</value>
- public PackageTargetSystem targetSystem { get; set; }
-
- /// <summary>
- /// The guid of the assembly associated with this package (if a plug-in).
- /// This is used to identify the proper item for automatic updates.
- /// </summary>
- /// <value>The name.</value>
- public string guid { get; set; }
-
- /// <summary>
- /// Gets or sets the total number of ratings for this package.
- /// </summary>
- /// <value>The total ratings.</value>
- public int? totalRatings { get; set; }
-
- /// <summary>
- /// Gets or sets the average rating for this package .
- /// </summary>
- /// <value>The rating.</value>
- public float avgRating { get; set; }
-
- /// <summary>
- /// Gets or sets whether or not this package is registered.
- /// </summary>
- /// <value>True if registered.</value>
- public bool isRegistered { get; set; }
-
- /// <summary>
- /// Gets or sets the expiration date for this package.
- /// </summary>
- /// <value>Expiration Date.</value>
- public DateTime expDate { get; set; }
-
- /// <summary>
- /// Gets or sets the versions.
- /// </summary>
- /// <value>The versions.</value>
- public PackageVersionInfo[] versions { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable in application store].
- /// </summary>
- /// <value><c>true</c> if [enable in application store]; otherwise, <c>false</c>.</value>
- public bool enableInAppStore { get; set; }
-
- /// <summary>
- /// Gets or sets the installs.
- /// </summary>
- /// <value>The installs.</value>
- public int installs { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PackageInfo"/> class.
- /// </summary>
- public PackageInfo()
- {
- versions = new PackageVersionInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Updates/PackageTargetSystem.cs b/MediaBrowser.Model/Updates/PackageTargetSystem.cs
deleted file mode 100644
index c80dddde3..000000000
--- a/MediaBrowser.Model/Updates/PackageTargetSystem.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Enum PackageType
- /// </summary>
- public enum PackageTargetSystem
- {
- /// <summary>
- /// Server
- /// </summary>
- Server,
- /// <summary>
- /// MB Theater
- /// </summary>
- MBTheater,
- /// <summary>
- /// MB Classic
- /// </summary>
- MBClassic
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Updates/PackageVersionClass.cs b/MediaBrowser.Model/Updates/PackageVersionClass.cs
deleted file mode 100644
index 3f51e1b3c..000000000
--- a/MediaBrowser.Model/Updates/PackageVersionClass.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Enum PackageVersionClass
- /// </summary>
- public enum PackageVersionClass
- {
- /// <summary>
- /// The release
- /// </summary>
- Release = 0,
- /// <summary>
- /// The beta
- /// </summary>
- Beta = 1,
- /// <summary>
- /// The dev
- /// </summary>
- Dev = 2
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
deleted file mode 100644
index 3ac518187..000000000
--- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Model.Updates
-{
- /// <summary>
- /// Class PackageVersionInfo
- /// </summary>
- public class PackageVersionInfo
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string name { get; set; }
-
- /// <summary>
- /// Gets or sets the guid.
- /// </summary>
- /// <value>The guid.</value>
- public string guid { get; set; }
-
- /// <summary>
- /// Gets or sets the version STR.
- /// </summary>
- /// <value>The version STR.</value>
- public string versionStr { get; set; }
-
- /// <summary>
- /// The _version
- /// </summary>
- private Version _version;
- /// <summary>
- /// Gets or sets the version.
- /// Had to make this an interpreted property since Protobuf can't handle Version
- /// </summary>
- /// <value>The version.</value>
- [IgnoreDataMember]
- public Version version
- {
- get { return _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1"))); }
- }
-
- /// <summary>
- /// Values the or default.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="def">The def.</param>
- /// <returns>System.String.</returns>
- private static string ValueOrDefault(string str, string def)
- {
- return string.IsNullOrEmpty(str) ? def : str;
- }
-
- /// <summary>
- /// Gets or sets the classification.
- /// </summary>
- /// <value>The classification.</value>
- public PackageVersionClass classification { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- /// <value>The description.</value>
- public string description { get; set; }
-
- /// <summary>
- /// Gets or sets the required version STR.
- /// </summary>
- /// <value>The required version STR.</value>
- public string requiredVersionStr { get; set; }
-
- /// <summary>
- /// Gets or sets the source URL.
- /// </summary>
- /// <value>The source URL.</value>
- public string sourceUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the source URL.
- /// </summary>
- /// <value>The source URL.</value>
- public string checksum { get; set; }
-
- /// <summary>
- /// Gets or sets the target filename.
- /// </summary>
- /// <value>The target filename.</value>
- public string targetFilename { get; set; }
-
- public string infoUrl { get; set; }
-
- public string runtimes { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Users/AuthenticationResult.cs b/MediaBrowser.Model/Users/AuthenticationResult.cs
deleted file mode 100644
index b4a7cab2a..000000000
--- a/MediaBrowser.Model/Users/AuthenticationResult.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Model.Users
-{
- public class AuthenticationResult
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public UserDto User { get; set; }
-
- /// <summary>
- /// Gets or sets the session information.
- /// </summary>
- /// <value>The session information.</value>
- public SessionInfoDto SessionInfo { get; set; }
-
- /// <summary>
- /// Gets or sets the authentication token.
- /// </summary>
- /// <value>The authentication token.</value>
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Users/ForgotPasswordAction.cs b/MediaBrowser.Model/Users/ForgotPasswordAction.cs
deleted file mode 100644
index f75b1d74b..000000000
--- a/MediaBrowser.Model/Users/ForgotPasswordAction.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace MediaBrowser.Model.Users
-{
- public enum ForgotPasswordAction
- {
- ContactAdmin = 0,
- PinCode = 1,
- InNetworkRequired = 2
- }
-}
diff --git a/MediaBrowser.Model/Users/ForgotPasswordResult.cs b/MediaBrowser.Model/Users/ForgotPasswordResult.cs
deleted file mode 100644
index 7dbb1e96b..000000000
--- a/MediaBrowser.Model/Users/ForgotPasswordResult.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Users
-{
- public class ForgotPasswordResult
- {
- /// <summary>
- /// Gets or sets the action.
- /// </summary>
- /// <value>The action.</value>
- public ForgotPasswordAction Action { get; set; }
- /// <summary>
- /// Gets or sets the pin file.
- /// </summary>
- /// <value>The pin file.</value>
- public string PinFile { get; set; }
- /// <summary>
- /// Gets or sets the pin expiration date.
- /// </summary>
- /// <value>The pin expiration date.</value>
- public DateTime? PinExpirationDate { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Users/PinRedeemResult.cs b/MediaBrowser.Model/Users/PinRedeemResult.cs
deleted file mode 100644
index 6a01bf2d4..000000000
--- a/MediaBrowser.Model/Users/PinRedeemResult.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-namespace MediaBrowser.Model.Users
-{
- public class PinRedeemResult
- {
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="PinRedeemResult"/> is success.
- /// </summary>
- /// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
- public bool Success { get; set; }
- /// <summary>
- /// Gets or sets the users reset.
- /// </summary>
- /// <value>The users reset.</value>
- public string[] UsersReset { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Users/UserAction.cs b/MediaBrowser.Model/Users/UserAction.cs
deleted file mode 100644
index 680835364..000000000
--- a/MediaBrowser.Model/Users/UserAction.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Users
-{
- public class UserAction
- {
- public string Id { get; set; }
- public string ServerId { get; set; }
- public string UserId { get; set; }
- public string ItemId { get; set; }
- public UserActionType Type { get; set; }
- public DateTime Date { get; set; }
- public long? PositionTicks { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Users/UserActionType.cs b/MediaBrowser.Model/Users/UserActionType.cs
deleted file mode 100644
index 493de6272..000000000
--- a/MediaBrowser.Model/Users/UserActionType.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Model.Users
-{
- public enum UserActionType
- {
- PlayedItem = 0
- }
-}
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
deleted file mode 100644
index de2e9cc04..000000000
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Model.Users
-{
- public class UserPolicy
- {
- /// <summary>
- /// Gets or sets a value indicating whether this instance is administrator.
- /// </summary>
- /// <value><c>true</c> if this instance is administrator; otherwise, <c>false</c>.</value>
- public bool IsAdministrator { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- public bool IsHidden { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is disabled.
- /// </summary>
- /// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
- public bool IsDisabled { get; set; }
-
- /// <summary>
- /// Gets or sets the max parental rating.
- /// </summary>
- /// <value>The max parental rating.</value>
- public int? MaxParentalRating { get; set; }
-
- public string[] BlockedTags { get; set; }
- public bool EnableUserPreferenceAccess { get; set; }
- public AccessSchedule[] AccessSchedules { get; set; }
- public UnratedItem[] BlockUnratedItems { get; set; }
- public bool EnableRemoteControlOfOtherUsers { get; set; }
- public bool EnableSharedDeviceControl { get; set; }
-
- public bool EnableLiveTvManagement { get; set; }
- public bool EnableLiveTvAccess { get; set; }
-
- public bool EnableMediaPlayback { get; set; }
- public bool EnableAudioPlaybackTranscoding { get; set; }
- public bool EnableVideoPlaybackTranscoding { get; set; }
- public bool EnablePlaybackRemuxing { get; set; }
-
- public bool EnableContentDeletion { get; set; }
- public string[] EnableContentDeletionFromFolders { get; set; }
- public bool EnableContentDownloading { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable synchronize].
- /// </summary>
- /// <value><c>true</c> if [enable synchronize]; otherwise, <c>false</c>.</value>
- public bool EnableSyncTranscoding { get; set; }
-
- public string[] EnabledDevices { get; set; }
- public bool EnableAllDevices { get; set; }
-
- public string[] EnabledChannels { get; set; }
- public bool EnableAllChannels { get; set; }
-
- public string[] EnabledFolders { get; set; }
- public bool EnableAllFolders { get; set; }
-
- public int InvalidLoginAttemptCount { get; set; }
-
- public bool EnablePublicSharing { get; set; }
-
- public string[] BlockedMediaFolders { get; set; }
- public string[] BlockedChannels { get; set; }
-
- public int RemoteClientBitrateLimit { get; set; }
-
- public UserPolicy()
- {
- EnableContentDeletion = true;
- EnableContentDeletionFromFolders = new string[] { };
-
- EnableSyncTranscoding = true;
-
- EnableMediaPlayback = true;
- EnableAudioPlaybackTranscoding = true;
- EnableVideoPlaybackTranscoding = true;
- EnablePlaybackRemuxing = true;
-
- EnableLiveTvManagement = true;
- EnableLiveTvAccess = true;
-
- // Without this on by default, admins won't be able to do this
- // Improve in the future
- EnableLiveTvManagement = true;
-
- EnableSharedDeviceControl = true;
-
- BlockedTags = new string[] { };
- BlockUnratedItems = new UnratedItem[] { };
-
- EnableUserPreferenceAccess = true;
-
- AccessSchedules = new AccessSchedule[] { };
-
- EnableAllChannels = true;
- EnabledChannels = new string[] { };
-
- EnableAllFolders = true;
- EnabledFolders = new string[] { };
-
- EnabledDevices = new string[] { };
- EnableAllDevices = true;
-
- EnableContentDownloading = true;
- EnablePublicSharing = true;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs
deleted file mode 100644
index b9628ec3e..000000000
--- a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Xml;
-
-namespace MediaBrowser.Model.Xml
-{
- public interface IXmlReaderSettingsFactory
- {
- XmlReaderSettings Create(bool enableValidation);
- }
-}
diff --git a/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
deleted file mode 100644
index 09546e4b6..000000000
--- a/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
-using System.Linq;
-
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Providers.Books
-{
- public class AudioPodcastMetadataService : MetadataService<AudioPodcast, SongInfo>
- {
- protected override void MergeData(MetadataResult<AudioPodcast> source, MetadataResult<AudioPodcast> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-
- var sourceItem = source.Item;
- var targetItem = target.Item;
-
- if (replaceData || targetItem.Artists.Length == 0)
- {
- targetItem.Artists = sourceItem.Artists;
- }
-
- if (replaceData || string.IsNullOrEmpty(targetItem.Album))
- {
- targetItem.Album = sourceItem.Album;
- }
- }
-
- public AudioPodcastMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
- {
- }
- }
-}
diff --git a/MediaBrowser.Providers/Books/GoogleBooksProvider.cs b/MediaBrowser.Providers/Books/GoogleBooksProvider.cs
deleted file mode 100644
index 7330b8c43..000000000
--- a/MediaBrowser.Providers/Books/GoogleBooksProvider.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Providers;
-
-namespace MediaBrowser.Providers.Books
-{
- public class GoogleBooksProvider : IRemoteMetadataProvider<AudioBook, SongInfo>
- {
- public string Name => "Google Books";
- private readonly IHttpClient _httpClient;
-
- public GoogleBooksProvider(IHttpClient httpClient)
- {
- _httpClient = httpClient;
- }
-
- public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url,
- BufferContent = false
- });
- }
-
- public async Task<MetadataResult<AudioBook>> GetMetadata(SongInfo info, CancellationToken cancellationToken)
- {
- return new MetadataResult<AudioBook>();
- }
-
- public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SongInfo searchInfo, CancellationToken cancellationToken)
- {
- return new List<RemoteSearchResult>();
- }
- }
-}
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 2c3dc0e31..78791906a 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -9,27 +9,15 @@ using MediaBrowser.Providers.Manager;
using System.Linq;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using System.Collections.Generic;
namespace MediaBrowser.Providers.BoxSets
{
public class BoxSetMetadataService : MetadataService<BoxSet, BoxSetInfo>
{
- protected override ItemUpdateType BeforeSaveInternal(BoxSet item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected override IList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
{
- var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
-
- if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
- {
- if (!item.LockedFields.Contains(MetadataFields.OfficialRating))
- {
- if (item.UpdateRatingToContent())
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
- }
- }
-
- return updateType;
+ return item.GetLinkedChildren();
}
protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
@@ -42,12 +30,59 @@ namespace MediaBrowser.Providers.BoxSets
if (mergeMetadataSettings)
{
targetItem.LinkedChildren = sourceItem.LinkedChildren;
- targetItem.Shares = sourceItem.Shares;
}
}
+ protected override ItemUpdateType BeforeSaveInternal(BoxSet item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ {
+ var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
+
+ var libraryFolderIds = item.GetLibraryFolderIds();
+
+ var itemLibraryFolderIds = item.LibraryFolderIds;
+ if (itemLibraryFolderIds == null || !libraryFolderIds.SequenceEqual(itemLibraryFolderIds))
+ {
+ item.LibraryFolderIds = libraryFolderIds;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
+ return updateType;
+ }
+
public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
+
+ protected override bool EnableUpdatingGenresFromChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override bool EnableUpdatingOfficialRatingFromChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override bool EnableUpdatingStudiosFromChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override bool EnableUpdatingPremiereDateFromChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
}
}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index 7d1f2779b..729897290 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is BoxSet;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.BoxSets
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.BoxSets
{
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
return GetImages(mainResult, language, tmdbImageUrl);
}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
index 17b0646ed..634329177 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.BoxSets
{
public class MovieDbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo>
{
- private const string GetCollectionInfo3 = @"https://api.themoviedb.org/3/collection/{0}?api_key={1}&append_to_response=images";
+ private const string GetCollectionInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/collection/{0}?api_key={1}&append_to_response=images";
internal static MovieDbBoxSetProvider Current;
@@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.BoxSets
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var result = new RemoteSearchResult
{
@@ -189,7 +189,7 @@ namespace MediaBrowser.Providers.BoxSets
{
using (var json = response.Content)
{
- mainResult = _json.DeserializeFromStream<RootObject>(json);
+ mainResult = await _json.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
}
}
@@ -217,7 +217,7 @@ namespace MediaBrowser.Providers.BoxSets
{
using (var json = response.Content)
{
- mainResult = _json.DeserializeFromStream<RootObject>(json);
+ mainResult = await _json.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
}
}
}
@@ -225,7 +225,6 @@ namespace MediaBrowser.Providers.BoxSets
return mainResult;
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureInfo(string tmdbId, string preferredMetadataLanguage, CancellationToken cancellationToken)
{
var path = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage);
@@ -235,9 +234,9 @@ namespace MediaBrowser.Providers.BoxSets
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
index 1bbc6fa4e..3d0c7c964 100644
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs
@@ -33,11 +33,6 @@ namespace MediaBrowser.Providers.Chapters
_itemRepo = itemRepo;
}
- public List<ChapterInfo> GetChapters(string itemId)
- {
- return _itemRepo.GetChapters(new Guid(itemId));
- }
-
public void SaveChapters(string itemId, List<ChapterInfo> chapters)
{
_itemRepo.SaveChapters(new Guid(itemId), chapters);
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index 1dab08671..e472a23c4 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -24,17 +24,4 @@ namespace MediaBrowser.Providers.Folders
{
}
}
-
- public class ManualCollectionsFolderMetadataService : MetadataService<ManualCollectionsFolder, ItemLookupInfo>
- {
- protected override void MergeData(MetadataResult<ManualCollectionsFolder> source, MetadataResult<ManualCollectionsFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
-
- public ManualCollectionsFolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
- {
- }
- }
-
}
diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
deleted file mode 100644
index 74c01fb5c..000000000
--- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Providers.ImagesByName
-{
- public static class ImageUtils
- {
- /// <summary>
- /// Ensures the list.
- /// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="file">The file.</param>
- /// <param name="httpClient">The HTTP client.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public static async Task<string> EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, CancellationToken cancellationToken)
- {
- var fileInfo = fileSystem.GetFileInfo(file);
-
- if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
- {
- var temp = await httpClient.GetTempFile(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Progress = new SimpleProgress<double>(),
- Url = url
-
- }).ConfigureAwait(false);
-
- fileSystem.CreateDirectory(fileSystem.GetDirectoryName(file));
-
- try
- {
- fileSystem.CopyFile(temp, file, true);
- }
- catch
- {
-
- }
-
- return temp;
- }
-
- return file;
- }
-
- public static string FindMatch(IHasMetadata item, IEnumerable<string> images)
- {
- var name = GetComparableName(item.Name);
-
- return images.FirstOrDefault(i => string.Equals(name, GetComparableName(i), StringComparison.OrdinalIgnoreCase));
- }
-
- private static string GetComparableName(string name)
- {
- return name.Replace(" ", string.Empty)
- .Replace(".", string.Empty)
- .Replace("&", string.Empty)
- .Replace("!", string.Empty)
- .Replace(",", string.Empty)
- .Replace("/", string.Empty);
- }
-
- public static IEnumerable<string> GetAvailableImages(string file, IFileSystem fileSystem)
- {
- using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
- {
- using (var reader = new StreamReader(fileStream))
- {
- var lines = new List<string>();
-
- while (!reader.EndOfStream)
- {
- var text = reader.ReadLine();
-
- if (!string.IsNullOrWhiteSpace(text))
- {
- lines.Add(text);
- }
- }
-
- return lines;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
deleted file mode 100644
index 509c91188..000000000
--- a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Providers.LiveTv
-{
- public class AudioRecordingService : MetadataService<LiveTvAudioRecording, ItemLookupInfo>
- {
- protected override void MergeData(MetadataResult<LiveTvAudioRecording> source, MetadataResult<LiveTvAudioRecording> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
-
- public AudioRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
- {
- }
- }
-}
diff --git a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
deleted file mode 100644
index 31e3ecaf4..000000000
--- a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Providers.LiveTv
-{
- public class ChannelMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo>
- {
- protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
-
- public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
- {
- }
- }
-}
diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
index 28a12540b..dfb0c58ad 100644
--- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
@@ -5,21 +5,18 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.LiveTv
{
- public class ProgramMetadataService : MetadataService<LiveTvProgram, LiveTvProgramLookupInfo>
+ public class LiveTvMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo>
{
- protected override void MergeData(MetadataResult<LiveTvProgram> source, MetadataResult<LiveTvProgram> target, 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);
}
- public ProgramMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
+ public LiveTvMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
}
diff --git a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
deleted file mode 100644
index 8bfa91655..000000000
--- a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Manager;
-using System.Collections.Generic;
-
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Providers.LiveTv
-{
- public class VideoRecordingService : MetadataService<LiveTvVideoRecording, ItemLookupInfo>
- {
- protected override void MergeData(MetadataResult<LiveTvVideoRecording> source, MetadataResult<LiveTvVideoRecording> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
-
- public VideoRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
- {
- }
- }
-}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 4bf5e9208..c4d0c4929 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -39,7 +39,6 @@ namespace MediaBrowser.Providers.Manager
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
/// <summary>
/// Initializes a new instance of the <see cref="ImageSaver" /> class.
@@ -48,13 +47,12 @@ namespace MediaBrowser.Providers.Manager
/// <param name="libraryMonitor">The directory watchers.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="logger">The logger.</param>
- public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamFactory memoryStreamProvider)
+ public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{
_config = config;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_logger = logger;
- _memoryStreamProvider = memoryStreamProvider;
}
/// <summary>
@@ -68,19 +66,19 @@ namespace MediaBrowser.Providers.Manager
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">mimeType</exception>
- public Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
}
- public async Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
+ public async Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(mimeType))
{
throw new ArgumentNullException("mimeType");
}
- var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.IsOwnedItem && !(item is Audio);
+ var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValue && !(item is Audio);
if (item is User)
{
@@ -92,8 +90,7 @@ namespace MediaBrowser.Providers.Manager
saveLocally = false;
}
- var locationType = item.LocationType;
- if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
+ if (!item.IsFileProtocol)
{
saveLocally = false;
@@ -127,7 +124,7 @@ namespace MediaBrowser.Providers.Manager
var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false);
// If there are more than one output paths, the stream will need to be seekable
- var memoryStream = _memoryStreamProvider.CreateNew();
+ var memoryStream = new MemoryStream();
using (source)
{
await source.CopyToAsync(memoryStream).ConfigureAwait(false);
@@ -236,7 +233,7 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns>
private async Task SaveImageToLocation(Stream source, string path, CancellationToken cancellationToken)
{
- _logger.Info("Saving image to {0}", path);
+ _logger.Debug("Saving image to {0}", path);
var parentFolder = _fileSystem.GetDirectoryName(path);
@@ -287,7 +284,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(IHasMetadata item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
+ private string[] GetSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
if (!saveLocally || (_config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy))
{
@@ -309,7 +306,7 @@ namespace MediaBrowser.Providers.Manager
/// or
/// imageIndex
/// </exception>
- private ItemImageInfo GetCurrentImage(IHasMetadata item, ImageType type, int imageIndex)
+ private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex)
{
return item.GetImageInfo(type, imageIndex);
}
@@ -324,7 +321,7 @@ namespace MediaBrowser.Providers.Manager
/// <exception cref="System.ArgumentNullException">imageIndex
/// or
/// imageIndex</exception>
- private void SetImagePath(IHasMetadata item, ImageType type, int? imageIndex, string path)
+ private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path)
{
item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
}
@@ -343,7 +340,7 @@ namespace MediaBrowser.Providers.Manager
/// or
/// imageIndex
/// </exception>
- private string GetStandardSavePath(IHasMetadata item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
+ private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
var season = item as Season;
var extension = MimeTypes.ToExtension(mimeType);
@@ -416,7 +413,7 @@ namespace MediaBrowser.Providers.Manager
filename = item is MusicAlbum ? "cdart" : "disc";
break;
case ImageType.Primary:
- filename = item is Episode ? _fileSystem.GetFileNameWithoutExtension(item.Path) : folderName;
+ filename = saveLocally && item is Episode ? _fileSystem.GetFileNameWithoutExtension(item.Path) : folderName;
break;
case ImageType.Backdrop:
filename = GetBackdropSaveFilename(item.GetImages(type), "backdrop", "backdrop", imageIndex);
@@ -496,7 +493,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(IHasMetadata item, ImageType type, int? imageIndex, string mimeType)
+ private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType)
{
var season = item as Season;
@@ -616,7 +613,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(IHasMetadata item, ImageType type, string imageFilename, string extension)
+ private string GetSavePathForItemInMixedFolder(BaseItem 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 1d432463b..c80d43841 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -24,6 +24,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Channels;
namespace MediaBrowser.Providers.Manager
{
@@ -42,7 +43,7 @@ namespace MediaBrowser.Providers.Manager
_fileSystem = fileSystem;
}
- public bool ValidateImages(IHasMetadata item, IEnumerable<IImageProvider> providers, IDirectoryService directoryService)
+ public bool ValidateImages(BaseItem item, IEnumerable<IImageProvider> providers, IDirectoryService directoryService)
{
var hasChanges = false;
@@ -61,7 +62,7 @@ namespace MediaBrowser.Providers.Manager
return hasChanges;
}
- public async Task<RefreshResult> RefreshImages(IHasMetadata item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
+ public async Task<RefreshResult> RefreshImages(BaseItem item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
{
if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
{
@@ -74,11 +75,12 @@ namespace MediaBrowser.Providers.Manager
var result = new RefreshResult { UpdateType = ItemUpdateType.None };
- var providerIds = new List<Guid>();
+ var typeName = item.GetType().Name;
+ var typeOptions = libraryOptions.GetTypeOptions(typeName) ?? new TypeOptions { Type = typeName };
// In order to avoid duplicates, only download these if there are none already
- var backdropLimit = savedOptions.GetLimit(ImageType.Backdrop);
- var screenshotLimit = savedOptions.GetLimit(ImageType.Screenshot);
+ var backdropLimit = typeOptions.GetLimit(ImageType.Backdrop);
+ var screenshotLimit = typeOptions.GetLimit(ImageType.Screenshot);
var downloadedImages = new List<ImageType>();
foreach (var provider in providers)
@@ -87,8 +89,7 @@ namespace MediaBrowser.Providers.Manager
if (remoteProvider != null)
{
- await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, savedOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
- providerIds.Add(provider.GetType().FullName.GetMD5());
+ await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
continue;
}
@@ -96,31 +97,21 @@ namespace MediaBrowser.Providers.Manager
if (dynamicImageProvider != null)
{
- await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, savedOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
- providerIds.Add(provider.GetType().FullName.GetMD5());
+ await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
}
}
- result.Providers = providerIds;
-
return result;
}
/// <summary>
/// Refreshes from provider.
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="provider">The provider.</param>
- /// <param name="refreshOptions">The refresh options.</param>
- /// <param name="savedOptions">The saved options.</param>
- /// <param name="downloadedImages">The downloaded images.</param>
- /// <param name="result">The result.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task RefreshFromProvider(IHasMetadata item,
+ private async Task RefreshFromProvider(BaseItem item,
IDynamicImageProvider provider,
ImageRefreshOptions refreshOptions,
- MetadataOptions savedOptions,
+ TypeOptions savedOptions,
+ LibraryOptions libraryOptions,
ICollection<ImageType> downloadedImages,
RefreshResult result,
CancellationToken cancellationToken)
@@ -202,7 +193,7 @@ namespace MediaBrowser.Providers.Manager
ImageType.Thumb
};
- private bool HasImage(IHasMetadata item, ImageType type)
+ private bool HasImage(BaseItem item, ImageType type)
{
return item.HasImage(type);
}
@@ -216,7 +207,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(IHasMetadata item, List<ImageType> images, MetadataOptions savedOptions, int backdropLimit, int screenshotLimit)
+ private bool ContainsImages(BaseItem item, List<ImageType> images, TypeOptions savedOptions, int backdropLimit, int screenshotLimit)
{
if (_singularImages.Any(i => images.Contains(i) && !HasImage(item, i) && savedOptions.GetLimit(i) > 0))
{
@@ -249,10 +240,10 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task RefreshFromProvider(IHasMetadata item, LibraryOptions libraryOptions,
+ private async Task RefreshFromProvider(BaseItem item, LibraryOptions libraryOptions,
IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions,
- MetadataOptions savedOptions,
+ TypeOptions savedOptions,
int backdropLimit,
int screenshotLimit,
ICollection<ImageType> downloadedImages,
@@ -267,7 +258,7 @@ namespace MediaBrowser.Providers.Manager
}
if (!refreshOptions.ReplaceAllImages &&
- refreshOptions.ReplaceImages.Count == 0 &&
+ refreshOptions.ReplaceImages.Length == 0 &&
ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
{
return;
@@ -302,20 +293,14 @@ namespace MediaBrowser.Providers.Manager
}
}
- if (!item.LockedFields.Contains(MetadataFields.Backdrops))
- {
- minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
- await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
- }
+ minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
+ await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
- if (!item.LockedFields.Contains(MetadataFields.Screenshots))
+ var hasScreenshots = item as IHasScreenshots;
+ if (hasScreenshots != null)
{
- var hasScreenshots = item as IHasScreenshots;
- if (hasScreenshots != null)
- {
- minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
- await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
- }
+ minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
+ await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -329,34 +314,12 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool IsEnabled(MetadataOptions options, ImageType type, IHasMetadata item)
+ private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item)
{
- if (type == ImageType.Backdrop)
- {
- if (item.LockedFields.Contains(MetadataFields.Backdrops))
- {
- return false;
- }
- }
- else if (type == ImageType.Screenshot)
- {
- if (item.LockedFields.Contains(MetadataFields.Screenshots))
- {
- return false;
- }
- }
- else
- {
- if (item.LockedFields.Contains(MetadataFields.Images))
- {
- return false;
- }
- }
-
return options.IsEnabled(type);
}
- private void ClearImages(IHasMetadata item, ImageType type)
+ private void ClearImages(BaseItem item, ImageType type)
{
var deleted = false;
var deletedImages = new List<ItemImageInfo>();
@@ -376,7 +339,7 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
-
+
}
}
@@ -388,7 +351,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- public bool MergeImages(IHasMetadata item, List<LocalImageInfo> images)
+ public bool MergeImages(BaseItem item, List<LocalImageInfo> images)
{
var changed = false;
@@ -457,7 +420,7 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
- private bool UpdateMultiImages(IHasMetadata item, List<LocalImageInfo> images, ImageType type)
+ private bool UpdateMultiImages(BaseItem item, List<LocalImageInfo> images, ImageType type)
{
var changed = false;
@@ -475,7 +438,7 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
- private async Task<bool> DownloadImage(IHasMetadata item, LibraryOptions libraryOptions,
+ private async Task<bool> DownloadImage(BaseItem item, LibraryOptions libraryOptions,
IRemoteImageProvider provider,
RefreshResult result,
IEnumerable<RemoteImageInfo> images,
@@ -521,14 +484,14 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- private bool EnableImageStub(IHasMetadata item, ImageType type, LibraryOptions libraryOptions)
+ private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions)
{
if (item is LiveTvProgram)
{
return true;
}
- if (item.LocationType == LocationType.Remote || item.LocationType == LocationType.Virtual)
+ if (!item.IsFileProtocol)
{
return true;
}
@@ -555,14 +518,14 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- private void SaveImageStub(IHasMetadata item, ImageType imageType, IEnumerable<string> urls)
+ private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls)
{
var newIndex = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
SaveImageStub(item, imageType, urls, newIndex);
}
- private void SaveImageStub(IHasMetadata item, ImageType imageType, IEnumerable<string> urls, int newIndex)
+ private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex)
{
var path = string.Join("|", urls.Take(1).ToArray());
@@ -574,7 +537,7 @@ namespace MediaBrowser.Providers.Manager
}, newIndex);
}
- private async Task DownloadBackdrops(IHasMetadata item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
+ private async Task DownloadBackdrops(BaseItem 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 4e72240f2..57711d3b6 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -18,7 +18,7 @@ using MediaBrowser.Providers.MediaInfo;
namespace MediaBrowser.Providers.Manager
{
public abstract class MetadataService<TItemType, TIdType> : IMetadataService
- where TItemType : IHasMetadata, IHasLookupInfo<TIdType>, new()
+ where TItemType : BaseItem, IHasLookupInfo<TIdType>, new()
where TIdType : ItemLookupInfo, new()
{
protected readonly IServerConfigurationManager ServerConfigurationManager;
@@ -27,7 +27,6 @@ namespace MediaBrowser.Providers.Manager
protected readonly IFileSystem FileSystem;
protected readonly IUserDataManager UserDataManager;
protected readonly ILibraryManager LibraryManager;
- private readonly SubtitleResolver _subtitleResolver;
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager)
{
@@ -37,11 +36,22 @@ namespace MediaBrowser.Providers.Manager
FileSystem = fileSystem;
UserDataManager = userDataManager;
LibraryManager = libraryManager;
+ }
- _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, fileSystem);
+ private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService)
+ {
+ try
+ {
+ return directoryService.GetFile(path);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting file {0}", ex, path);
+ return null;
+ }
}
- public async Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
+ public async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
var config = ProviderManager.GetMetadataOptions(item);
@@ -49,46 +59,22 @@ namespace MediaBrowser.Providers.Manager
var updateType = ItemUpdateType.None;
var requiresRefresh = false;
- var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
+ var libraryOptions = LibraryManager.GetLibraryOptions(item);
if (!requiresRefresh && libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
{
requiresRefresh = true;
}
- DateTime? newDateModified = null;
- if (item.LocationType == LocationType.FileSystem)
- {
- var file = refreshOptions.DirectoryService.GetFile(item.Path);
- if (file != null)
- {
- newDateModified = file.LastWriteTimeUtc;
- if (item.EnableRefreshOnDateModifiedChange)
- {
- if (newDateModified != item.DateModified)
- {
- Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, newDateModified, item.Id);
- requiresRefresh = true;
- }
- }
-
- if (!requiresRefresh && item.SupportsLocalMetadata)
- {
- var video = item as Video;
-
- if (video != null && !video.IsPlaceHolder)
- {
- requiresRefresh = !video.SubtitleFiles
- .SequenceEqual(_subtitleResolver.GetExternalSubtitleFiles(video, refreshOptions.DirectoryService, false), StringComparer.Ordinal);
- }
- }
- }
- }
-
if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
// TODO: If this returns true, should we instead just change metadata refresh mode to Full?
requiresRefresh = item.RequiresRefresh();
+
+ if (requiresRefresh)
+ {
+ Logger.Debug("Refreshing {0} {1} because item.RequiresRefresh() returned true", typeof(TItemType).Name, item.Path ?? item.Name);
+ }
}
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
@@ -108,7 +94,10 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
localImagesFailed = true;
- Logger.ErrorException("Error validating images for {0}", ex, item.Path ?? item.Name ?? "Unknown name");
+ if (!(item is IItemByName))
+ {
+ Logger.ErrorException("Error validating images for {0}", ex, item.Path ?? item.Name ?? "Unknown name");
+ }
}
var metadataResult = new MetadataResult<TItemType>
@@ -123,12 +112,12 @@ namespace MediaBrowser.Providers.Manager
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
- var providers = GetProviders(item, refreshOptions, isFirstRefresh, requiresRefresh)
+ var providers = GetProviders(item, libraryOptions, refreshOptions, isFirstRefresh, requiresRefresh)
.ToList();
if (providers.Count > 0 || isFirstRefresh || requiresRefresh)
{
- if (item.BeforeMetadataRefresh())
+ if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata))
{
updateType = updateType | ItemUpdateType.MetadataImport;
}
@@ -157,7 +146,7 @@ namespace MediaBrowser.Providers.Manager
}
// Next run remote image providers, but only if local image providers didn't throw an exception
- if (!localImagesFailed && refreshOptions.ImageRefreshMode != ImageRefreshMode.ValidationOnly)
+ if (!localImagesFailed && refreshOptions.ImageRefreshMode != MetadataRefreshMode.ValidationOnly)
{
var providers = GetNonLocalImageProviders(item, allImageProviders, refreshOptions).ToList();
@@ -173,17 +162,21 @@ namespace MediaBrowser.Providers.Manager
}
}
- var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType);
+ var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
updateType = updateType | beforeSaveResult;
- if (newDateModified.HasValue)
- {
- item.DateModified = newDateModified.Value;
- }
-
// Save if changes were made, or it's never been saved before
if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
{
+ if (item.IsFileProtocol)
+ {
+ var file = TryGetFile(item.Path, refreshOptions.DirectoryService);
+ if (file != null)
+ {
+ item.DateModified = file.LastWriteTimeUtc;
+ }
+ }
+
// If any of these properties are set then make sure the updateType is not None, just to force everything to save
if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
{
@@ -200,7 +193,7 @@ namespace MediaBrowser.Providers.Manager
}
// Save to database
- await SaveItem(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
+ SaveItem(metadataResult, libraryOptions, updateType, cancellationToken);
}
await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -215,19 +208,19 @@ namespace MediaBrowser.Providers.Manager
lookupInfo.Year = result.ProductionYear;
}
- protected async Task SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
+ protected void SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
{
if (result.Item.SupportsPeople && result.People != null)
{
- var baseItem = result.Item as BaseItem;
+ var baseItem = result.Item;
LibraryManager.UpdatePeople(baseItem, result.People);
- await SavePeopleMetadata(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
+ SavePeopleMetadata(result.People, libraryOptions, cancellationToken);
}
result.Item.UpdateToRepository(reason, cancellationToken);
}
- private async Task SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
+ private void SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
{
foreach (var person in people)
{
@@ -250,7 +243,7 @@ namespace MediaBrowser.Providers.Manager
if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
{
- await AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false);
+ AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken);
saveEntity = true;
updateType = updateType | ItemUpdateType.ImageUpdate;
@@ -264,20 +257,20 @@ namespace MediaBrowser.Providers.Manager
}
}
- private async Task AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
+ private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
{
- if (libraryOptions.DownloadImagesInAdvance)
- {
- try
- {
- await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
- return;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in AddPersonImage", ex);
- }
- }
+ //if (libraryOptions.DownloadImagesInAdvance)
+ //{
+ // try
+ // {
+ // await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
+ // return;
+ // }
+ // catch (Exception ex)
+ // {
+ // Logger.ErrorException("Error in AddPersonImage", ex);
+ // }
+ //}
personEntity.SetImage(new ItemImageInfo
{
@@ -286,11 +279,10 @@ namespace MediaBrowser.Providers.Manager
}, 0);
}
- private readonly Task _cachedTask = Task.FromResult(true);
protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
item.AfterMetadataRefresh();
- return _cachedTask;
+ return Task.CompletedTask;
}
/// <summary>
@@ -309,12 +301,17 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- protected virtual ItemUpdateType BeforeSaveInternal(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected virtual ItemUpdateType BeforeSaveInternal(TItemType item, bool isFullRefresh, ItemUpdateType updateType)
{
- var updateType = ItemUpdateType.None;
+ if (EnableUpdateMetadataFromChildren(item, isFullRefresh, updateType))
+ {
+ if (isFullRefresh || updateType > ItemUpdateType.None)
+ {
+ var children = GetChildrenForMetadataUpdates(item);
- updateType |= SaveCumulativeRunTimeTicks(item, isFullRefresh, currentUpdateType);
- updateType |= SaveDateLastMediaAdded(item, isFullRefresh, currentUpdateType);
+ updateType = UpdateMetadataFromChildren(item, children, isFullRefresh, updateType);
+ }
+ }
var presentationUniqueKey = item.CreatePresentationUniqueKey();
if (!string.Equals(item.PresentationUniqueKey, presentationUniqueKey, StringComparison.Ordinal))
@@ -326,42 +323,119 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType SaveCumulativeRunTimeTicks(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected virtual bool EnableUpdateMetadataFromChildren(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ {
+ if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
+ {
+ if (EnableUpdatingPremiereDateFromChildren || EnableUpdatingGenresFromChildren || EnableUpdatingStudiosFromChildren || EnableUpdatingOfficialRatingFromChildren)
+ {
+ return true;
+ }
+ var folder = item as Folder;
+ if (folder != null)
+ {
+ return folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks;
+ }
+ }
+
+ return false;
+ }
+
+ protected virtual IList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
+ {
+ var folder = item as Folder;
+ if (folder != null)
+ {
+ return folder.GetRecursiveChildren();
+ }
+
+ return new List<BaseItem>();
+ }
+
+ protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
{
var updateType = ItemUpdateType.None;
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
- var folder = item as Folder;
- if (folder != null && folder.SupportsCumulativeRunTimeTicks)
+ updateType |= UpdateCumulativeRunTimeTicks(item, children);
+ updateType |= UpdateDateLastMediaAdded(item, children);
+
+ if (EnableUpdatingPremiereDateFromChildren)
{
- var items = folder.GetRecursiveChildren(i => !i.IsFolder);
- var ticks = items.Select(i => i.RunTimeTicks ?? 0).Sum();
+ updateType |= UpdatePremiereDate(item, children);
+ }
+
+ if (EnableUpdatingGenresFromChildren)
+ {
+ updateType |= UpdateGenres(item, children);
+ }
+
+ if (EnableUpdatingStudiosFromChildren)
+ {
+ updateType |= UpdateStudios(item, children);
+ }
+
+ if (EnableUpdatingOfficialRatingFromChildren)
+ {
+ updateType |= UpdateOfficialRating(item, children);
+ }
+ }
- if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks)
+ return updateType;
+ }
+
+ private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList<BaseItem> children)
+ {
+ var folder = item as Folder;
+ if (folder != null && folder.SupportsCumulativeRunTimeTicks)
+ {
+ long ticks = 0;
+
+ foreach (var child in children)
+ {
+ if (!child.IsFolder)
{
- folder.RunTimeTicks = ticks;
- updateType = ItemUpdateType.MetadataEdit;
+ ticks += (child.RunTimeTicks ?? 0);
}
}
+
+ if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks)
+ {
+ folder.RunTimeTicks = ticks;
+ return ItemUpdateType.MetadataEdit;
+ }
}
- return updateType;
+ return ItemUpdateType.None;
}
- private ItemUpdateType SaveDateLastMediaAdded(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
var folder = item as Folder;
if (folder != null && folder.SupportsDateLastMediaAdded)
{
- var items = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).ToList();
- var date = items.Count == 0 ? (DateTime?)null : items.Max();
+ DateTime dateLastMediaAdded = DateTime.MinValue;
+ var any = false;
+
+ foreach (var child in children)
+ {
+ if (!child.IsFolder)
+ {
+ var childDateCreated = child.DateCreated;
+ if (childDateCreated > dateLastMediaAdded)
+ {
+ dateLastMediaAdded = childDateCreated;
+ }
+ any = true;
+ }
+ }
- if ((!folder.DateLastMediaAdded.HasValue && date.HasValue) || folder.DateLastMediaAdded != date)
+ if ((!folder.DateLastMediaAdded.HasValue && any) || folder.DateLastMediaAdded != dateLastMediaAdded)
{
- folder.DateLastMediaAdded = date;
+ folder.DateLastMediaAdded = dateLastMediaAdded;
updateType = ItemUpdateType.MetadataImport;
}
}
@@ -369,14 +443,138 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
+ protected virtual bool EnableUpdatingPremiereDateFromChildren
+ {
+ get
+ {
+ return false;
+ }
+ }
+ protected virtual bool EnableUpdatingGenresFromChildren
+ {
+ get
+ {
+ return false;
+ }
+ }
+ protected virtual bool EnableUpdatingStudiosFromChildren
+ {
+ get
+ {
+ return false;
+ }
+ }
+ protected virtual bool EnableUpdatingOfficialRatingFromChildren
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
+ {
+ var updateType = ItemUpdateType.None;
+
+ if (children.Count == 0)
+ {
+ return updateType;
+ }
+
+ var date = children.Select(i => i.PremiereDate ?? DateTime.MaxValue).Min();
+
+ var originalPremiereDate = item.PremiereDate;
+ var originalProductionYear = item.ProductionYear;
+
+ if (date > DateTime.MinValue && date < DateTime.MaxValue)
+ {
+ item.PremiereDate = date;
+ item.ProductionYear = date.Year;
+ }
+ else
+ {
+ var year = children.Select(i => i.ProductionYear ?? 0).Min();
+
+ if (year > 0)
+ {
+ item.ProductionYear = year;
+ }
+ }
+
+ if ((originalPremiereDate ?? DateTime.MinValue) != (item.PremiereDate ?? DateTime.MinValue) ||
+ (originalProductionYear ?? -1) != (item.ProductionYear ?? -1))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+
+ return updateType;
+ }
+
+ private ItemUpdateType UpdateGenres(TItemType item, IList<BaseItem> children)
+ {
+ var updateType = ItemUpdateType.None;
+
+ if (!item.LockedFields.Contains(MetadataFields.Genres))
+ {
+ var currentList = item.Genres;
+
+ item.Genres = children.SelectMany(i => i.Genres)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ if (currentList.Length != item.Genres.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+ }
+
+ return updateType;
+ }
+
+ private ItemUpdateType UpdateStudios(TItemType item, IList<BaseItem> children)
+ {
+ var updateType = ItemUpdateType.None;
+
+ if (!item.LockedFields.Contains(MetadataFields.Studios))
+ {
+ var currentList = item.Studios;
+
+ item.Studios = children.SelectMany(i => i.Studios)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ if (currentList.Length != item.Studios.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+ }
+
+ return updateType;
+ }
+
+ private ItemUpdateType UpdateOfficialRating(TItemType item, IList<BaseItem> children)
+ {
+ var updateType = ItemUpdateType.None;
+
+ if (!item.LockedFields.Contains(MetadataFields.OfficialRating))
+ {
+ if (item.UpdateRatingToItems(children))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+ }
+
+ return updateType;
+ }
+
/// <summary>
/// Gets the providers.
/// </summary>
/// <returns>IEnumerable{`0}.</returns>
- protected IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh)
+ protected IEnumerable<IMetadataProvider> GetProviders(BaseItem item, LibraryOptions libraryOptions, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh)
{
// Get providers to refresh
- var providers = ((ProviderManager)ProviderManager).GetMetadataProviders<TItemType>(item).ToList();
+ var providers = ((ProviderManager)ProviderManager).GetMetadataProviders<TItemType>(item, libraryOptions).ToList();
var metadataRefreshMode = options.MetadataRefreshMode;
@@ -410,12 +608,18 @@ namespace MediaBrowser.Providers.Manager
var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>()
.Any();
+ var anyLocalProvidersChanged = providersWithChanges.OfType<ILocalMetadataProvider>()
+ .Any();
+
+ var anyLocalPreRefreshProvidersChanged = providersWithChanges.OfType<IPreRefreshProvider>()
+ .Any();
+
providers = providers.Where(i =>
{
// If any provider reports a change, always run local ones as well
if (i is ILocalMetadataProvider)
{
- return true;
+ return anyRemoteProvidersChanged || anyLocalProvidersChanged || anyLocalPreRefreshProvidersChanged;
}
// If any remote providers changed, run them all so that priorities can be honored
@@ -429,7 +633,7 @@ namespace MediaBrowser.Providers.Manager
return anyRemoteProvidersChanged;
}
- // Run custom providers if they report a change or any remote providers change
+ // Run custom refresh providers if they report a change or any remote providers change
return anyRemoteProvidersChanged || providersWithChanges.Contains(i);
}).ToList();
@@ -439,7 +643,7 @@ namespace MediaBrowser.Providers.Manager
return providers;
}
- protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(IHasMetadata item, IEnumerable<IImageProvider> allImageProviders, ImageRefreshOptions options)
+ protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(BaseItem item, IEnumerable<IImageProvider> allImageProviders, ImageRefreshOptions options)
{
// Get providers to refresh
var providers = allImageProviders.Where(i => !(i is ILocalImageProvider)).ToList();
@@ -447,7 +651,7 @@ namespace MediaBrowser.Providers.Manager
var dateLastImageRefresh = item.DateLastRefreshed;
// Run all if either of these flags are true
- var runAllProviders = options.ImageRefreshMode == ImageRefreshMode.FullRefresh || dateLastImageRefresh == default(DateTime);
+ var runAllProviders = options.ImageRefreshMode == MetadataRefreshMode.FullRefresh || dateLastImageRefresh == default(DateTime);
if (!runAllProviders)
{
@@ -468,7 +672,7 @@ namespace MediaBrowser.Providers.Manager
return providers;
}
- public bool CanRefresh(IHasMetadata item)
+ public bool CanRefresh(BaseItem item)
{
return item is TItemType;
}
@@ -487,14 +691,13 @@ namespace MediaBrowser.Providers.Manager
{
var refreshResult = new RefreshResult
{
- UpdateType = ItemUpdateType.None,
- Providers = providers.Select(i => i.GetType().FullName.GetMD5()).ToList()
+ UpdateType = ItemUpdateType.None
};
var item = metadata.Item;
var customProviders = providers.OfType<ICustomMetadataProvider<TItemType>>().ToList();
- var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
+ var logName = !item.IsFileProtocol ? item.Name ?? item.Path : item.Path ?? item.Name;
foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider))
{
@@ -606,7 +809,7 @@ namespace MediaBrowser.Providers.Manager
await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
}
- ImportUserData(item, userDataList, cancellationToken);
+ //ImportUserData(item, userDataList, cancellationToken);
return refreshResult;
}
@@ -621,19 +824,6 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- private void ImportUserData(TItemType item, List<UserItemData> userDataList, CancellationToken cancellationToken)
- {
- var hasUserData = item as IHasUserData;
-
- if (hasUserData != null)
- {
- foreach (var userData in userDataList)
- {
- UserDataManager.SaveUserData(userData.UserId, hasUserData, userData, UserDataSaveReason.Import, cancellationToken);
- }
- }
- }
-
private async Task RunCustomProvider(ICustomMetadataProvider<TItemType> provider, TItemType item, string logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken)
{
Logger.Debug("Running {0} for {1}", provider.GetType().Name, logName);
@@ -662,16 +852,17 @@ namespace MediaBrowser.Providers.Manager
{
var refreshResult = new RefreshResult();
- var results = new List<MetadataResult<TItemType>>();
+ var tmpDataMerged = false;
foreach (var provider in providers)
{
var providerName = provider.GetType().Name;
Logger.Debug("Running {0} for {1}", providerName, logName);
- if (id != null)
+ if (id != null && !tmpDataMerged)
{
MergeNewData(temp.Item, id);
+ tmpDataMerged = true;
}
try
@@ -682,7 +873,8 @@ namespace MediaBrowser.Providers.Manager
{
result.Provider = provider.Name;
- results.Add(result);
+ MergeData(result, temp, new MetadataFields[] { }, false, false);
+ MergeNewData(temp.Item, id);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
}
@@ -703,37 +895,6 @@ namespace MediaBrowser.Providers.Manager
}
}
- var orderedResults = new List<MetadataResult<TItemType>>();
- var preferredLanguage = NormalizeLanguage(id.MetadataLanguage);
-
- // prioritize results with matching ResultLanguage
- foreach (var result in results)
- {
- if (!result.QueriedById)
- {
- break;
- }
-
- if (string.Equals(NormalizeLanguage(result.ResultLanguage), preferredLanguage, StringComparison.OrdinalIgnoreCase) && result.QueriedById)
- {
- orderedResults.Add(result);
- }
- }
-
- // add all other results
- foreach (var result in results)
- {
- if (!orderedResults.Contains(result))
- {
- orderedResults.Add(result);
- }
- }
-
- foreach (var result in results)
- {
- MergeData(result, temp, new MetadataFields[] { }, false, false);
- }
-
return refreshResult;
}
@@ -775,7 +936,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool HasChanged(IHasMetadata item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
+ private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
{
try
{
@@ -800,7 +961,6 @@ namespace MediaBrowser.Providers.Manager
{
public ItemUpdateType UpdateType { get; set; }
public string ErrorMessage { get; set; }
- public List<Guid> Providers { get; set; }
public int Failures { get; set; }
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index cdef42771..4c9312b83 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -25,6 +25,7 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization;
using Priority_Queue;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller.Subtitles;
namespace MediaBrowser.Providers.Manager
{
@@ -67,22 +68,18 @@ namespace MediaBrowser.Providers.Manager
private IExternalId[] _externalIds;
private readonly Func<ILibraryManager> _libraryManagerFactory;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
+ private ISubtitleManager _subtitleManager;
+
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary>
- /// <param name="httpClient">The HTTP client.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="libraryMonitor">The directory watchers.</param>
- /// <param name="logManager">The log manager.</param>
- /// <param name="fileSystem">The file system.</param>
- public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json, IMemoryStreamFactory memoryStreamProvider)
+ public ProviderManager(IHttpClient httpClient, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json)
{
_logger = logManager.GetLogger("ProviderManager");
_httpClient = httpClient;
@@ -92,7 +89,7 @@ namespace MediaBrowser.Providers.Manager
_appPaths = appPaths;
_libraryManagerFactory = libraryManagerFactory;
_json = json;
- _memoryStreamProvider = memoryStreamProvider;
+ _subtitleManager = subtitleManager;
}
/// <summary>
@@ -116,7 +113,7 @@ namespace MediaBrowser.Providers.Manager
}).ToArray();
}
- public Task<ItemUpdateType> RefreshSingleItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public Task<ItemUpdateType> RefreshSingleItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
IMetadataService service = null;
var type = item.GetType();
@@ -151,7 +148,7 @@ namespace MediaBrowser.Providers.Manager
return Task.FromResult(ItemUpdateType.None);
}
- public async Task SaveImage(IHasMetadata item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public async Task SaveImage(BaseItem item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
using (var response = await _httpClient.GetResponse(new HttpRequestOptions
{
@@ -161,16 +158,26 @@ namespace MediaBrowser.Providers.Manager
}).ConfigureAwait(false))
{
+ // Workaround for tvheadend channel icons
+ // TODO: Isolate this hack into the tvh plugin
+ if (string.IsNullOrEmpty(response.ContentType))
+ {
+ if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ response.ContentType = "image/png";
+ }
+ }
+
await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken).ConfigureAwait(false);
}
}
- public Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
+ public Task SaveImage(BaseItem 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);
+ return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
}
- public Task SaveImage(IHasMetadata item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
+ public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(source))
{
@@ -179,10 +186,10 @@ namespace MediaBrowser.Providers.Manager
var fileStream = _fileSystem.GetFileStream(source, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
- return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
+ return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
- public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasMetadata item, RemoteImageQuery query, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
{
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);
@@ -217,7 +224,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(IHasMetadata item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null)
+ private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null)
{
try
{
@@ -253,7 +260,7 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
- public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasMetadata item)
+ public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(BaseItem item)
{
return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo
{
@@ -262,64 +269,70 @@ namespace MediaBrowser.Providers.Manager
});
}
- public IEnumerable<IImageProvider> GetImageProviders(IHasMetadata item, ImageRefreshOptions refreshOptions)
+ public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
{
- return GetImageProviders(item, GetMetadataOptions(item), refreshOptions, false);
+ return GetImageProviders(item, _libraryManagerFactory().GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false);
}
- private IEnumerable<IImageProvider> GetImageProviders(IHasMetadata item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
+ private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
{
// Avoid implicitly captured closure
var currentOptions = options;
- return ImageProviders.Where(i => CanRefresh(i, item, options, refreshOptions, includeDisabled))
- .OrderBy(i =>
- {
- // See if there's a user-defined order
- if (!(i is ILocalImageProvider))
- {
- var index = Array.IndexOf(currentOptions.ImageFetcherOrder, i.Name);
+ var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name);
+ var typeFetcherOrder = typeOptions == null ? null : typeOptions.ImageFetcherOrder;
- if (index != -1)
+ return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, options, refreshOptions, includeDisabled))
+ .OrderBy(i =>
+ {
+ // See if there's a user-defined order
+ if (!(i is ILocalImageProvider))
{
- return index;
+ var fetcherOrder = typeFetcherOrder ?? currentOptions.ImageFetcherOrder;
+
+ var index = Array.IndexOf(fetcherOrder, i.Name);
+
+ if (index != -1)
+ {
+ return index;
+ }
}
- }
- // Not configured. Just return some high number to put it at the end.
- return 100;
- })
+ // Not configured. Just return some high number to put it at the end.
+ return 100;
+ })
.ThenBy(GetOrder);
}
- public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(IHasMetadata item)
- where T : IHasMetadata
+ public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(BaseItem item, LibraryOptions libraryOptions)
+ where T : BaseItem
{
- var options = GetMetadataOptions(item);
+ var globalMetadataOptions = GetMetadataOptions(item);
- return GetMetadataProvidersInternal<T>(item, options, false, false, true);
+ return GetMetadataProvidersInternal<T>(item, libraryOptions, globalMetadataOptions, false, false);
}
- private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem)
- where T : IHasMetadata
+ private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(BaseItem item, LibraryOptions libraryOptions, MetadataOptions globalMetadataOptions, bool includeDisabled, bool forceEnableInternetMetadata)
+ where T : BaseItem
{
// Avoid implicitly captured closure
- var currentOptions = options;
+ var currentOptions = globalMetadataOptions;
return _metadataProviders.OfType<IMetadataProvider<T>>()
- .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, forceEnableInternetMetadata, checkIsOwnedItem))
- .OrderBy(i => GetConfiguredOrder(i, options))
+ .Where(i => CanRefresh(i, item, libraryOptions, currentOptions, includeDisabled, forceEnableInternetMetadata))
+ .OrderBy(i => GetConfiguredOrder(item, i, libraryOptions, globalMetadataOptions))
.ThenBy(GetDefaultOrder);
}
- private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasMetadata item, bool includeDisabled)
+ private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled)
{
var options = GetMetadataOptions(item);
+ var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item);
- return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>();
+ return GetImageProviders(item, libraryOptions, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>();
}
- private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem)
+ private bool CanRefresh(IMetadataProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata)
{
if (!includeDisabled)
{
@@ -331,12 +344,7 @@ namespace MediaBrowser.Providers.Manager
if (provider is IRemoteMetadataProvider)
{
- if (!forceEnableInternetMetadata && !item.IsInternetMetadataEnabled())
- {
- return false;
- }
-
- if (Array.IndexOf(options.DisabledMetadataFetchers, provider.Name) != -1)
+ if (!forceEnableInternetMetadata && !item.IsMetadataFetcherEnabled(libraryOptions, provider.Name))
{
return false;
}
@@ -349,7 +357,7 @@ namespace MediaBrowser.Providers.Manager
}
// If this restriction is ever lifted, movie xml providers will have to be updated to prevent owned items like trailers from reading those files
- if (checkIsOwnedItem && item.IsOwnedItem)
+ if (!item.OwnerId.Equals(Guid.Empty))
{
if (provider is ILocalMetadataProvider || provider is IRemoteMetadataProvider)
{
@@ -360,14 +368,14 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- private bool CanRefresh(IImageProvider provider, IHasMetadata item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
+ private bool CanRefresh(IImageProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
{
if (!includeDisabled)
{
// If locked only allow local providers
if (item.IsLocked && !(provider is ILocalImageProvider))
{
- if (refreshOptions.ImageRefreshMode != ImageRefreshMode.FullRefresh)
+ if (refreshOptions.ImageRefreshMode != MetadataRefreshMode.FullRefresh)
{
return false;
}
@@ -375,18 +383,10 @@ namespace MediaBrowser.Providers.Manager
if (provider is IRemoteImageProvider || provider is IDynamicImageProvider)
{
- if (Array.IndexOf(options.DisabledImageFetchers, provider.Name) != -1)
+ if (!item.IsImageFetcherEnabled(libraryOptions, provider.Name))
{
return false;
}
-
- if (provider is IRemoteImageProvider)
- {
- if (!refreshOptions.ForceEnableInternetMetadata && !item.IsInternetMetadataEnabled())
- {
- return false;
- }
- }
}
}
@@ -418,12 +418,14 @@ namespace MediaBrowser.Providers.Manager
return hasOrder.Order;
}
- private int GetConfiguredOrder(IMetadataProvider provider, MetadataOptions options)
+ private int GetConfiguredOrder(BaseItem item, IMetadataProvider provider, LibraryOptions libraryOptions, MetadataOptions globalMetadataOptions)
{
// See if there's a user-defined order
if (provider is ILocalMetadataProvider)
{
- var index = Array.IndexOf(options.LocalMetadataReaderOrder, provider.Name);
+ var configuredOrder = libraryOptions.LocalMetadataReaderOrder ?? globalMetadataOptions.LocalMetadataReaderOrder;
+
+ var index = Array.IndexOf(configuredOrder, provider.Name);
if (index != -1)
{
@@ -434,7 +436,12 @@ namespace MediaBrowser.Providers.Manager
// See if there's a user-defined order
if (provider is IRemoteMetadataProvider)
{
- var index = Array.IndexOf(options.MetadataFetcherOrder, provider.Name);
+ var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name);
+ var typeFetcherOrder = typeOptions == null ? null : typeOptions.MetadataFetcherOrder;
+
+ var fetcherOrder = typeFetcherOrder ?? globalMetadataOptions.MetadataFetcherOrder;
+
+ var index = Array.IndexOf(fetcherOrder, provider.Name);
if (index != -1)
{
@@ -470,22 +477,13 @@ namespace MediaBrowser.Providers.Manager
GetPluginSummary<Series>(),
GetPluginSummary<Season>(),
GetPluginSummary<Episode>(),
- GetPluginSummary<Person>(),
GetPluginSummary<MusicAlbum>(),
GetPluginSummary<MusicArtist>(),
GetPluginSummary<Audio>(),
GetPluginSummary<AudioBook>(),
- GetPluginSummary<AudioPodcast>(),
- GetPluginSummary<Genre>(),
GetPluginSummary<Studio>(),
- GetPluginSummary<GameGenre>(),
- GetPluginSummary<MusicGenre>(),
GetPluginSummary<MusicVideo>(),
- GetPluginSummary<Video>(),
- GetPluginSummary<LiveTvChannel>(),
- GetPluginSummary<LiveTvProgram>(),
- GetPluginSummary<LiveTvVideoRecording>(),
- GetPluginSummary<LiveTvAudioRecording>()
+ GetPluginSummary<Video>()
};
}
@@ -506,13 +504,24 @@ namespace MediaBrowser.Providers.Manager
ItemType = typeof(T).Name
};
- var imageProviders = GetImageProviders(dummy, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), true).ToList();
+ var libraryOptions = new LibraryOptions();
+
+ var imageProviders = GetImageProviders(dummy, libraryOptions, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), true).ToList();
var pluginList = summary.Plugins.ToList();
- AddMetadataPlugins(pluginList, dummy, options);
+ AddMetadataPlugins(pluginList, dummy, libraryOptions, options);
AddImagePlugins(pluginList, dummy, imageProviders);
+ var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy);
+
+ // Subtitle fetchers
+ pluginList.AddRange(subtitleProviders.Select(i => new MetadataPlugin
+ {
+ Name = i.Name,
+ Type = MetadataPluginType.SubtitleFetcher
+ }));
+
summary.Plugins = pluginList.ToArray(pluginList.Count);
var supportedImageTypes = imageProviders.OfType<IRemoteImageProvider>()
@@ -527,10 +536,10 @@ namespace MediaBrowser.Providers.Manager
return summary;
}
- private void AddMetadataPlugins<T>(List<MetadataPlugin> list, T item, MetadataOptions options)
- where T : IHasMetadata
+ private void AddMetadataPlugins<T>(List<MetadataPlugin> list, T item, LibraryOptions libraryOptions, MetadataOptions options)
+ where T : BaseItem
{
- var providers = GetMetadataProvidersInternal<T>(item, options, true, false, false).ToList();
+ var providers = GetMetadataProvidersInternal<T>(item, libraryOptions, options, true, true).ToList();
// Locals
list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin
@@ -547,7 +556,7 @@ namespace MediaBrowser.Providers.Manager
}));
// Savers
- list.AddRange(_savers.Where(i => IsSaverEnabledForItem(i, item, ItemUpdateType.MetadataEdit, true)).OrderBy(i => i.Name).Select(i => new MetadataPlugin
+ list.AddRange(_savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, ItemUpdateType.MetadataEdit, true)).OrderBy(i => i.Name).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.MetadataSaver
@@ -555,7 +564,7 @@ namespace MediaBrowser.Providers.Manager
}
private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders)
- where T : IHasMetadata
+ where T : BaseItem
{
// Locals
@@ -565,17 +574,15 @@ namespace MediaBrowser.Providers.Manager
Type = MetadataPluginType.LocalImageProvider
}));
- var enableInternet = item.IsInternetMetadataEnabled();
-
// Fetchers
- list.AddRange(imageProviders.Where(i => i is IDynamicImageProvider || (enableInternet && i is IRemoteImageProvider)).Select(i => new MetadataPlugin
+ list.AddRange(imageProviders.Where(i => i is IDynamicImageProvider || (i is IRemoteImageProvider)).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.ImageFetcher
}));
}
- public MetadataOptions GetMetadataOptions(IHasMetadata item)
+ public MetadataOptions GetMetadataOptions(BaseItem item)
{
var type = item.GetType().Name;
@@ -587,7 +594,7 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Saves the metadata.
/// </summary>
- public void SaveMetadata(IHasMetadata item, ItemUpdateType updateType)
+ public void SaveMetadata(BaseItem item, ItemUpdateType updateType)
{
SaveMetadata(item, updateType, _savers);
}
@@ -595,7 +602,7 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Saves the metadata.
/// </summary>
- public void SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable<string> savers)
+ public void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<string> savers)
{
SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase)));
}
@@ -607,9 +614,11 @@ namespace MediaBrowser.Providers.Manager
/// <param name="updateType">Type of the update.</param>
/// <param name="savers">The savers.</param>
/// <returns>Task.</returns>
- private void SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
+ private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
{
- foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, updateType, false)))
+ var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item);
+
+ foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false)))
{
_logger.Debug("Saving {0} to {1}.", item.Path ?? item.Name, saver.Name);
@@ -660,49 +669,57 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Determines whether [is saver enabled for item] [the specified saver].
/// </summary>
- /// <param name="saver">The saver.</param>
- /// <param name="item">The item.</param>
- /// <param name="updateType">Type of the update.</param>
- /// <param name="includeDisabled">if set to <c>true</c> [include disabled].</param>
- /// <returns><c>true</c> if [is saver enabled for item] [the specified saver]; otherwise, <c>false</c>.</returns>
- private bool IsSaverEnabledForItem(IMetadataSaver saver, IHasMetadata item, ItemUpdateType updateType, bool includeDisabled)
+ private bool IsSaverEnabledForItem(IMetadataSaver saver, BaseItem item, LibraryOptions libraryOptions, ItemUpdateType updateType, bool includeDisabled)
{
var options = GetMetadataOptions(item);
try
{
- var isEnabledFor = saver.IsEnabledFor(item, updateType);
+ if (!saver.IsEnabledFor(item, updateType))
+ {
+ return false;
+ }
if (!includeDisabled)
{
- if (options.DisabledMetadataSavers.Contains(saver.Name, StringComparer.OrdinalIgnoreCase))
+ if (libraryOptions.MetadataSavers == null)
{
- return false;
- }
+ if (options.DisabledMetadataSavers.Contains(saver.Name, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
- if (!item.IsSaveLocalMetadataEnabled())
- {
- if (updateType >= ItemUpdateType.MetadataEdit)
+ if (!item.IsSaveLocalMetadataEnabled())
{
- var fileSaver = saver as IMetadataFileSaver;
+ if (updateType >= ItemUpdateType.MetadataEdit)
+ {
+ var fileSaver = saver as IMetadataFileSaver;
- // Manual edit occurred
- // Even if save local is off, save locally anyway if the metadata file already exists
- if (fileSaver == null || !isEnabledFor || !_fileSystem.FileExists(fileSaver.GetSavePath(item)))
+ // Manual edit occurred
+ // Even if save local is off, save locally anyway if the metadata file already exists
+ if (fileSaver == null || !_fileSystem.FileExists(fileSaver.GetSavePath(item)))
+ {
+ return false;
+ }
+ }
+ else
{
+ // Manual edit did not occur
+ // Since local metadata saving is disabled, consider it disabled
return false;
}
}
- else
+ }
+ else
+ {
+ if (!libraryOptions.MetadataSavers.Contains(saver.Name, StringComparer.OrdinalIgnoreCase))
{
- // Manual edit did not occur
- // Since local metadata saving is disabled, consider it disabled
return false;
}
}
}
- return isEnabledFor;
+ return true;
}
catch (Exception ex)
{
@@ -711,23 +728,48 @@ namespace MediaBrowser.Providers.Manager
}
}
- public async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo,
- CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, CancellationToken cancellationToken)
where TItemType : BaseItem, new()
where TLookupType : ItemLookupInfo
{
- // Give it a dummy path just so that it looks like a file system item
- var dummy = new TItemType
+ BaseItem referenceItem = null;
+
+ if (!searchInfo.ItemId.Equals(Guid.Empty))
{
- Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"),
- ParentId = Guid.NewGuid()
- };
+ referenceItem = _libraryManagerFactory().GetItemById(searchInfo.ItemId);
+ }
- dummy.SetParent(new Folder());
+ return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken);
+ }
- var options = GetMetadataOptions(dummy);
+ public async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken)
+ where TItemType : BaseItem, new()
+ where TLookupType : ItemLookupInfo
+ {
+ LibraryOptions libraryOptions;
- var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false, false)
+ if (referenceItem == null)
+ {
+ // Give it a dummy path just so that it looks like a file system item
+ var dummy = new TItemType
+ {
+ Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"),
+ ParentId = Guid.NewGuid()
+ };
+
+ dummy.SetParent(new Folder());
+
+ referenceItem = dummy;
+ libraryOptions = new LibraryOptions();
+ }
+ else
+ {
+ libraryOptions = _libraryManagerFactory().GetLibraryOptions(referenceItem);
+ }
+
+ var options = GetMetadataOptions(referenceItem);
+
+ var providers = GetMetadataProvidersInternal<TItemType>(referenceItem, libraryOptions, options, searchInfo.IncludeDisabledProviders, false)
.OfType<IRemoteSearchProvider<TLookupType>>();
if (!string.IsNullOrEmpty(searchInfo.SearchProviderName))
@@ -1006,15 +1048,6 @@ namespace MediaBrowser.Providers.Manager
// Try to throttle this a little bit.
await Task.Delay(100).ConfigureAwait(false);
- if (refreshItem.Item2.ValidateChildren)
- {
- var folder = item as Folder;
- if (folder != null)
- {
- await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
- }
- }
-
var artist = item as MusicArtist;
var task = artist == null
? RefreshItem(item, refreshItem.Item2, cancellationToken)
@@ -1082,7 +1115,7 @@ namespace MediaBrowser.Providers.Manager
.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
- ArtistIds = new[] { item.Id.ToString("N") },
+ ArtistIds = new[] { item.Id },
DtoOptions = new DtoOptions(false)
{
EnableImages = false
@@ -1110,10 +1143,10 @@ namespace MediaBrowser.Providers.Manager
}
}
- public Task RefreshFullItem(IHasMetadata item, MetadataRefreshOptions options,
+ public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options,
CancellationToken cancellationToken)
{
- return RefreshItem((BaseItem)item, options, cancellationToken);
+ return RefreshItem(item, options, cancellationToken);
}
private bool _disposed;
@@ -1125,7 +1158,6 @@ namespace MediaBrowser.Providers.Manager
{
_disposeCancellationTokenSource.Cancel();
}
- GC.SuppressFinalize(this);
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
index b727e1354..3a961fe0e 100644
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs
@@ -63,21 +63,12 @@ namespace MediaBrowser.Providers.Manager
if (!lockedFields.Contains(MetadataFields.Genres))
{
- if (replaceData || target.Genres.Count == 0)
+ if (replaceData || target.Genres.Length == 0)
{
target.Genres = source.Genres;
}
}
- if (replaceData || string.IsNullOrEmpty(target.HomePageUrl))
- {
- target.HomePageUrl = source.HomePageUrl;
- if (!string.IsNullOrWhiteSpace(target.HomePageUrl) && target.HomePageUrl.IndexOf("http", StringComparison.OrdinalIgnoreCase) != 0)
- {
- target.HomePageUrl = "http://" + target.HomePageUrl;
- }
- }
-
if (replaceData || !target.IndexNumber.HasValue)
{
target.IndexNumber = source.IndexNumber;
@@ -189,22 +180,29 @@ namespace MediaBrowser.Providers.Manager
MergeVideoInfo(source, target, lockedFields, replaceData);
MergeDisplayOrder(source, target, lockedFields, replaceData);
- //if (!lockedFields.Contains(MetadataFields.SortName))
+ if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
{
- if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
- {
- var forcedSortName = source.ForcedSortName;
+ var forcedSortName = source.ForcedSortName;
- if (!string.IsNullOrWhiteSpace(forcedSortName))
- {
- target.ForcedSortName = forcedSortName;
- }
+ if (!string.IsNullOrWhiteSpace(forcedSortName))
+ {
+ target.ForcedSortName = forcedSortName;
}
}
if (mergeMetadataSettings)
{
- MergeMetadataSettings(source, target);
+ target.LockedFields = source.LockedFields;
+ target.IsLocked = source.IsLocked;
+
+ // Grab the value if it's there, but if not then don't overwrite the default
+ if (source.DateCreated != default(DateTime))
+ {
+ target.DateCreated = source.DateCreated;
+ }
+
+ target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
+ target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
}
}
@@ -233,22 +231,6 @@ namespace MediaBrowser.Providers.Manager
}
}
- public static void MergeMetadataSettings(BaseItem source,
- BaseItem target)
- {
- target.LockedFields = source.LockedFields;
- target.IsLocked = source.IsLocked;
-
- // Grab the value if it's there, but if not then don't overwrite the default
- if (source.DateCreated != default(DateTime))
- {
- target.DateCreated = source.DateCreated;
- }
-
- target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
- target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
- }
-
private static void MergeDisplayOrder(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
{
var sourceHasDisplayOrder = source as IHasDisplayOrder;
@@ -256,7 +238,15 @@ namespace MediaBrowser.Providers.Manager
if (sourceHasDisplayOrder != null && targetHasDisplayOrder != null)
{
- targetHasDisplayOrder.DisplayOrder = sourceHasDisplayOrder.DisplayOrder;
+ if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
+ {
+ var displayOrder = sourceHasDisplayOrder.DisplayOrder;
+
+ if (!string.IsNullOrWhiteSpace(displayOrder))
+ {
+ targetHasDisplayOrder.DisplayOrder = displayOrder;
+ }
+ }
}
}
@@ -284,15 +274,9 @@ namespace MediaBrowser.Providers.Manager
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 || target.RemoteTrailers.Length == 0)
{
- if (replaceData || targetCast.RemoteTrailers.Length == 0)
- {
- targetCast.RemoteTrailers = sourceCast.RemoteTrailers;
- }
+ target.RemoteTrailers = source.RemoteTrailers;
}
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index ae4499350..480d6c678 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -1,172 +1,22 @@
-<?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>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Providers</RootNamespace>
- <AssemblyName>MediaBrowser.Providers</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProductVersion>10.0.0</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <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>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Books\AudioBookMetadataService.cs" />
- <Compile Include="Books\AudioPodcastMetadataService.cs" />
- <Compile Include="Books\BookMetadataService.cs" />
- <Compile Include="Books\GoogleBooksProvider.cs" />
- <Compile Include="BoxSets\BoxSetMetadataService.cs" />
- <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
- <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
- <Compile Include="Channels\ChannelMetadataService.cs" />
- <Compile Include="Chapters\ChapterManager.cs" />
- <Compile Include="Folders\CollectionFolderMetadataService.cs" />
- <Compile Include="Folders\FolderMetadataService.cs" />
- <Compile Include="Folders\UserViewMetadataService.cs" />
- <Compile Include="GameGenres\GameGenreMetadataService.cs" />
- <Compile Include="Games\GameMetadataService.cs" />
- <Compile Include="Games\GameSystemMetadataService.cs" />
- <Compile Include="Genres\GenreMetadataService.cs" />
- <Compile Include="LiveTv\AudioRecordingService.cs" />
- <Compile Include="LiveTv\ChannelMetadataService.cs" />
- <Compile Include="LiveTv\ProgramMetadataService.cs" />
- <Compile Include="LiveTv\VideoRecordingService.cs" />
- <Compile Include="Manager\GenericPriorityQueue.cs" />
- <Compile Include="Manager\GenericPriorityQueueNode.cs" />
- <Compile Include="Manager\IFixedSizePriorityQueue.cs" />
- <Compile Include="Manager\ImageSaver.cs" />
- <Compile Include="Manager\IPriorityQueue.cs" />
- <Compile Include="Manager\ItemImageProvider.cs" />
- <Compile Include="Manager\ProviderManager.cs" />
- <Compile Include="Manager\MetadataService.cs" />
- <Compile Include="Manager\SimplePriorityQueue.cs" />
- <Compile Include="MediaInfo\FFProbeAudioInfo.cs" />
- <Compile Include="MediaInfo\FFProbeProvider.cs" />
- <Compile Include="MediaInfo\FFProbeVideoInfo.cs" />
- <Compile Include="MediaInfo\SubtitleDownloader.cs" />
- <Compile Include="MediaInfo\SubtitleResolver.cs" />
- <Compile Include="MediaInfo\SubtitleScheduledTask.cs" />
- <Compile Include="Movies\MovieDbTrailerProvider.cs" />
- <Compile Include="Movies\MovieExternalIds.cs" />
- <Compile Include="Movies\GenericMovieDbInfo.cs" />
- <Compile Include="Movies\MovieDbSearch.cs" />
- <Compile Include="Movies\MovieMetadataService.cs" />
- <Compile Include="Movies\TmdbSettings.cs" />
- <Compile Include="ImagesByName\ImageUtils.cs" />
- <Compile Include="MediaInfo\AudioImageProvider.cs" />
- <Compile Include="MediaInfo\VideoImageProvider.cs" />
- <Compile Include="Movies\MovieDbImageProvider.cs" />
- <Compile Include="Movies\FanartMovieImageProvider.cs" />
- <Compile Include="MusicGenres\MusicGenreMetadataService.cs" />
- <Compile Include="Music\AlbumMetadataService.cs" />
- <Compile Include="Music\ArtistMetadataService.cs" />
- <Compile Include="Music\AudioDbAlbumImageProvider.cs" />
- <Compile Include="Music\AudioDbAlbumProvider.cs" />
- <Compile Include="Music\AudioDbArtistImageProvider.cs" />
- <Compile Include="Music\AudioDbArtistProvider.cs" />
- <Compile Include="Music\AudioDbExternalIds.cs" />
- <Compile Include="Music\AudioMetadataService.cs" />
- <Compile Include="Music\Extensions.cs" />
- <Compile Include="Music\MovieDbMusicVideoProvider.cs" />
- <Compile Include="Music\MusicBrainzArtistProvider.cs" />
- <Compile Include="Music\MusicExternalIds.cs" />
- <Compile Include="Music\MusicVideoMetadataService.cs" />
- <Compile Include="Omdb\OmdbImageProvider.cs" />
- <Compile Include="Omdb\OmdbProvider.cs" />
- <Compile Include="Omdb\OmdbItemProvider.cs" />
- <Compile Include="People\MovieDbPersonImageProvider.cs" />
- <Compile Include="Movies\MovieDbProvider.cs" />
- <Compile Include="Music\FanArtAlbumProvider.cs" />
- <Compile Include="Music\FanArtArtistProvider.cs" />
- <Compile Include="Music\MusicBrainzAlbumProvider.cs" />
- <Compile Include="People\PersonMetadataService.cs" />
- <Compile Include="People\MovieDbPersonProvider.cs" />
- <Compile Include="Photos\PhotoAlbumMetadataService.cs" />
- <Compile Include="Photos\PhotoMetadataService.cs" />
- <Compile Include="Playlists\PlaylistMetadataService.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Manager\ProviderUtils.cs" />
- <Compile Include="Studios\StudiosImageProvider.cs" />
- <Compile Include="Studios\StudioMetadataService.cs" />
- <Compile Include="Subtitles\SubtitleManager.cs" />
- <Compile Include="TV\DummySeasonProvider.cs" />
- <Compile Include="TV\EpisodeMetadataService.cs" />
- <Compile Include="TV\FanArt\FanArtSeasonProvider.cs" />
- <Compile Include="TV\FanArt\FanartSeriesProvider.cs" />
- <Compile Include="TV\MissingEpisodeProvider.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbProviderBase.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbEpisodeImageProvider.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbSeasonProvider.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbSeriesImageProvider.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbSeriesProvider.cs" />
- <Compile Include="TV\TheMovieDb\MovieDbEpisodeProvider.cs" />
- <Compile Include="TV\Omdb\OmdbEpisodeProvider.cs" />
- <Compile Include="TV\SeriesMetadataService.cs" />
- <Compile Include="TV\TheTVDB\TvdbEpisodeImageProvider.cs" />
- <Compile Include="People\TvdbPersonImageProvider.cs" />
- <Compile Include="TV\TheTVDB\TvdbSeasonImageProvider.cs" />
- <Compile Include="TV\TheTVDB\TvdbSeriesImageProvider.cs" />
- <Compile Include="TV\SeasonMetadataService.cs" />
- <Compile Include="TV\TheTVDB\TvdbEpisodeProvider.cs" />
- <Compile Include="TV\TheTVDB\TvdbSeriesProvider.cs" />
- <Compile Include="TV\TheTVDB\TvdbPrescanTask.cs" />
- <Compile Include="TV\TvExternalIds.cs" />
- <Compile Include="Users\UserMetadataService.cs" />
- <Compile Include="Videos\VideoMetadataService.cs" />
- <Compile Include="Years\YearMetadataService.cs" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\DvdLib\DvdLib.csproj" />
</ItemGroup>
+
<ItemGroup>
- <ProjectReference Include="..\DvdLib\DvdLib.csproj">
- <Project>{713f42b5-878e-499d-a878-e4c652b1d5e8}</Project>
- <Name>DvdLib</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>
+ <Compile Include="..\SharedVersion.cs" />
</ItemGroup>
- <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
+
+ <ItemGroup>
+ <PackageReference Include="PlaylistsNET" Version="1.0.2" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets b/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.Providers/MediaBrowser.Providers.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.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index 1a650082c..2c0d5bcbc 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -32,12 +32,12 @@ namespace MediaBrowser.Providers.MediaInfo
_fileSystem = fileSystem;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType> { ImageType.Primary };
}
- public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
var audio = (Audio)item;
@@ -92,27 +92,22 @@ namespace MediaBrowser.Providers.MediaInfo
private string GetAudioImagePath(Audio item)
{
- string filename;
+ string filename = null;
if (item.GetType() == typeof(Audio))
{
- filename = item.Album ?? string.Empty;
- filename += string.Join(",", item.Artists);
+ var albumArtist = item.AlbumArtists.FirstOrDefault();
- if (!string.IsNullOrWhiteSpace(item.Album))
+ if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist))
{
- filename += "_" + item.Album;
- }
- else if (!string.IsNullOrWhiteSpace(item.Name))
- {
- filename += "_" + item.Name;
+ filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N");
}
else
{
- filename += "_" + item.Id.ToString("N");
+ filename = item.Id.ToString("N");
}
- filename = filename.GetMD5() + ".jpg";
+ filename += ".jpg";
}
else
{
@@ -138,11 +133,20 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Image Extractor"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
+ if (item.IsShortcut)
+ {
+ return false;
+ }
+ if (!item.IsFileProtocol)
+ {
+ return false;
+ }
+
var audio = item as Audio;
- return item.LocationType == LocationType.FileSystem && audio != null;
+ return audio != null;
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index cb0075b33..b3fc2a9d7 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
@@ -10,10 +10,12 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using System.Collections.Generic;
using System.Globalization;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
+using MediaBrowser.Model.Dto;
+using System;
+using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -24,65 +26,52 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IApplicationPaths _appPaths;
private readonly IJsonSerializer _json;
private readonly ILibraryManager _libraryManager;
+ private readonly IMediaSourceManager _mediaSourceManager;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public FFProbeAudioInfo(IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager)
+ public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager)
{
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_appPaths = appPaths;
_json = json;
_libraryManager = libraryManager;
+ _mediaSourceManager = mediaSourceManager;
}
- public async Task<ItemUpdateType> Probe<T>(T item, CancellationToken cancellationToken)
+ public async Task<ItemUpdateType> Probe<T>(T item, MetadataRefreshOptions options,
+ CancellationToken cancellationToken)
where T : Audio
{
- var result = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
+ var path = item.Path;
+ var protocol = item.PathProtocol ?? MediaProtocol.File;
- cancellationToken.ThrowIfCancellationRequested();
-
- Fetch(item, cancellationToken, result);
-
- return ItemUpdateType.MetadataImport;
- }
+ if (!item.IsShortcut || options.EnableRemoteContentProbe)
+ {
+ if (item.IsShortcut)
+ {
+ path = item.ShortcutPath;
+ protocol = _mediaSourceManager.GetPathProtocol(path);
+ }
- private const string SchemaVersion = "3";
+ var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ {
+ MediaType = DlnaProfileType.Audio,
+ MediaSource = new MediaSourceInfo
+ {
+ Path = path,
+ Protocol = protocol
+ }
- private async Task<Model.MediaInfo.MediaInfo> GetMediaInfo(BaseItem item, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- //var idString = item.Id.ToString("N");
- //var cachePath = Path.Combine(_appPaths.CachePath,
- // "ffprobe-audio",
- // idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
-
- //try
- //{
- // return _json.DeserializeFromFile<Model.MediaInfo.MediaInfo>(cachePath);
- //}
- //catch (FileNotFoundException)
- //{
-
- //}
- //catch (DirectoryNotFoundException)
- //{
- //}
-
- var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
- {
- InputPath = item.Path,
- MediaType = DlnaProfileType.Audio,
- Protocol = MediaProtocol.File
+ }, cancellationToken).ConfigureAwait(false);
- }, cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
- //Directory.CreateDirectory(_fileSystem.GetDirectoryName(cachePath));
- //_json.SerializeToFile(result, cachePath);
+ Fetch(item, cancellationToken, result);
+ }
- return result;
+ return ItemUpdateType.MetadataImport;
}
/// <summary>
@@ -156,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!audio.LockedFields.Contains(MetadataFields.Genres))
{
- audio.Genres.Clear();
+ audio.Genres = Array.Empty<string>();
foreach (var genre in data.Genres)
{
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 6a2677b43..f7cbf7211 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -21,22 +21,21 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Controller.Channels;
namespace MediaBrowser.Providers.MediaInfo
{
public class FFProbeProvider : ICustomMetadataProvider<Episode>,
ICustomMetadataProvider<MusicVideo>,
ICustomMetadataProvider<Movie>,
- ICustomMetadataProvider<LiveTvVideoRecording>,
- ICustomMetadataProvider<LiveTvAudioRecording>,
ICustomMetadataProvider<Trailer>,
ICustomMetadataProvider<Video>,
ICustomMetadataProvider<Audio>,
- ICustomMetadataProvider<AudioPodcast>,
ICustomMetadataProvider<AudioBook>,
IHasOrder,
IForcedProvider,
- IPreRefreshProvider
+ IPreRefreshProvider,
+ IHasItemChangeMonitor
{
private readonly ILogger _logger;
private readonly IIsoManager _isoManager;
@@ -52,28 +51,59 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ISubtitleManager _subtitleManager;
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
+ private readonly IChannelManager _channelManager;
+ private readonly IMediaSourceManager _mediaSourceManager;
public string Name
{
get { return "ffprobe"; }
}
- public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
- return FetchVideoInfo(item, options, cancellationToken);
+ var video = item as Video;
+ if (video == null || video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso)
+ {
+ var path = item.Path;
+
+ if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
+ {
+ var file = directoryService.GetFile(path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ _logger.Debug("Refreshing {0} due to date modified timestamp change.", path);
+ return true;
+ }
+ }
+ }
+
+ if (item.SupportsLocalMetadata)
+ {
+ if (video != null && !video.IsPlaceHolder)
+ {
+ if (!video.SubtitleFiles
+ .SequenceEqual(_subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal))
+ {
+ _logger.Debug("Refreshing {0} due to external subtitles change.", item.Path);
+ return true;
+ }
+ }
+ }
+
+ return false;
}
- public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return FetchVideoInfo(item, options, cancellationToken);
}
- public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return FetchVideoInfo(item, options, cancellationToken);
}
- public Task<ItemUpdateType> FetchAsync(LiveTvVideoRecording item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return FetchVideoInfo(item, options, cancellationToken);
}
@@ -90,25 +120,16 @@ namespace MediaBrowser.Providers.MediaInfo
public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
- return FetchAudioInfo(item, cancellationToken);
- }
-
- public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- return FetchAudioInfo(item, cancellationToken);
- }
-
- public Task<ItemUpdateType> FetchAsync(AudioPodcast item, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- return FetchAudioInfo(item, cancellationToken);
+ return FetchAudioInfo(item, options, cancellationToken);
}
public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
- return FetchAudioInfo(item, cancellationToken);
+ return FetchAudioInfo(item, options, cancellationToken);
}
- public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
+ private SubtitleResolver _subtitleResolver;
+ public FFProbeProvider(ILogger logger, IMediaSourceManager mediaSourceManager, IChannelManager channelManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
{
_logger = logger;
_isoManager = isoManager;
@@ -124,28 +145,37 @@ namespace MediaBrowser.Providers.MediaInfo
_subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
+ _channelManager = channelManager;
+ _mediaSourceManager = mediaSourceManager;
+
+ _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, fileSystem);
}
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Video
{
- if (item.LocationType != LocationType.FileSystem)
+ if (item.VideoType == VideoType.Iso)
{
return _cachedTask;
}
- if (item.VideoType == VideoType.Iso && !_isoManager.CanMount(item.Path))
+ if (item.IsPlaceHolder)
{
return _cachedTask;
}
- if (item.IsPlaceHolder)
+ if (!item.IsCompleteMedia)
{
return _cachedTask;
}
- if (!item.IsCompleteMedia)
+ if (item.IsVirtualItem)
+ {
+ return _cachedTask;
+ }
+
+ if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
{
return _cachedTask;
}
@@ -155,31 +185,47 @@ namespace MediaBrowser.Providers.MediaInfo
FetchShortcutInfo(item);
}
- var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager);
+ var prober = new FFProbeVideoInfo(_logger, _mediaSourceManager, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager);
return prober.ProbeVideo(item, options, cancellationToken);
}
- private void FetchShortcutInfo(Video video)
+ private string NormalizeStrmLine(string line)
{
- video.ShortcutPath = _fileSystem.ReadAllText(video.Path)
- .Replace("\t", string.Empty)
+ return line.Replace("\t", string.Empty)
.Replace("\r", string.Empty)
.Replace("\n", string.Empty)
.Trim();
}
- public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken)
+ private void FetchShortcutInfo(BaseItem item)
+ {
+ item.ShortcutPath = _fileSystem.ReadAllLines(item.Path)
+ .Select(NormalizeStrmLine)
+ .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith("#", StringComparison.OrdinalIgnoreCase));
+ }
+
+ public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Audio
{
- if (item.LocationType != LocationType.FileSystem)
+ if (item.IsVirtualItem)
+ {
+ return _cachedTask;
+ }
+
+ if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
{
return _cachedTask;
}
- var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
+ if (item.IsShortcut)
+ {
+ FetchShortcutInfo(item);
+ }
+
+ var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
- return prober.Probe(item, cancellationToken);
+ return prober.Probe(item, options, cancellationToken);
}
public int Order
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 443eb6eda..c7fc086be 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -26,6 +26,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Dto;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -45,8 +46,9 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ISubtitleManager _subtitleManager;
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
+ private readonly IMediaSourceManager _mediaSourceManager;
- public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
+ public FFProbeVideoInfo(ILogger logger, IMediaSourceManager mediaSourceManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
{
_logger = logger;
_isoManager = isoManager;
@@ -62,6 +64,7 @@ namespace MediaBrowser.Providers.MediaInfo
_subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
+ _mediaSourceManager = mediaSourceManager;
}
public async Task<ItemUpdateType> ProbeVideo<T>(T item,
@@ -69,93 +72,81 @@ namespace MediaBrowser.Providers.MediaInfo
CancellationToken cancellationToken)
where T : Video
{
- var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);
-
BlurayDiscInfo blurayDiscInfo = null;
- try
+ Model.MediaInfo.MediaInfo mediaInfoResult = null;
+
+ if (!item.IsShortcut || options.EnableRemoteContentProbe)
{
- Model.MediaInfo.MediaInfo mediaInfoResult = null;
+ string[] streamFileNames = null;
- if (!item.IsShortcut)
+ if (item.VideoType == VideoType.Dvd)
{
- string[] streamFileNames = null;
-
- if (item.VideoType == VideoType.Iso)
- {
- item.IsoType = DetermineIsoType(isoMount);
- }
+ streamFileNames = FetchFromDvdLib(item);
- if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
+ if (streamFileNames.Length == 0)
{
- streamFileNames = FetchFromDvdLib(item, isoMount);
-
- if (streamFileNames.Length == 0)
- {
- _logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
- return ItemUpdateType.MetadataImport;
- }
+ _logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
+ return ItemUpdateType.MetadataImport;
}
+ }
- else if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
- {
- var inputPath = isoMount != null ? isoMount.MountedPath : item.Path;
+ else if (item.VideoType == VideoType.BluRay)
+ {
+ var inputPath = item.Path;
- blurayDiscInfo = GetBDInfo(inputPath);
+ blurayDiscInfo = GetBDInfo(inputPath);
- streamFileNames = blurayDiscInfo.Files;
+ streamFileNames = blurayDiscInfo.Files;
- if (streamFileNames.Length == 0)
- {
- _logger.Error("No playable vobs found in bluray structure, skipping ffprobe.");
- return ItemUpdateType.MetadataImport;
- }
- }
-
- if (streamFileNames == null)
+ if (streamFileNames.Length == 0)
{
- streamFileNames = new string[] { };
+ _logger.Error("No playable vobs found in bluray structure, skipping ffprobe.");
+ return ItemUpdateType.MetadataImport;
}
-
- mediaInfoResult = await GetMediaInfo(item, isoMount, streamFileNames, cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
}
- await Fetch(item, cancellationToken, mediaInfoResult, isoMount, blurayDiscInfo, options).ConfigureAwait(false);
-
- }
- finally
- {
- if (isoMount != null)
+ if (streamFileNames == null)
{
- isoMount.Dispose();
+ streamFileNames = Array.Empty<string>();
}
+
+ mediaInfoResult = await GetMediaInfo(item, streamFileNames, cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
}
+ await Fetch(item, cancellationToken, mediaInfoResult, blurayDiscInfo, options).ConfigureAwait(false);
+
return ItemUpdateType.MetadataImport;
}
private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
- IIsoMount isoMount,
string[] streamFileNames,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- var protocol = item.LocationType == LocationType.Remote
- ? MediaProtocol.Http
- : MediaProtocol.File;
+ var path = item.Path;
+ var protocol = item.PathProtocol ?? MediaProtocol.File;
+
+ if (item.IsShortcut)
+ {
+ path = item.ShortcutPath;
+ protocol = _mediaSourceManager.GetPathProtocol(path);
+ }
return _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
PlayableStreamFileNames = streamFileNames,
- MountedIso = isoMount,
ExtractChapters = true,
- VideoType = item.VideoType,
MediaType = DlnaProfileType.Video,
- InputPath = item.Path,
- Protocol = protocol
+ MediaSource = new MediaSourceInfo
+ {
+ Path = path,
+ Protocol = protocol,
+ VideoType = item.VideoType
+ }
}, cancellationToken);
}
@@ -163,7 +154,6 @@ namespace MediaBrowser.Providers.MediaInfo
protected async Task Fetch(Video video,
CancellationToken cancellationToken,
Model.MediaInfo.MediaInfo mediaInfo,
- IIsoMount isoMount,
BlurayDiscInfo blurayInfo,
MetadataRefreshOptions options)
{
@@ -222,10 +212,11 @@ namespace MediaBrowser.Providers.MediaInfo
video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat;
}
- video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1260);
-
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ video.Height = videoStream == null ? 0 : videoStream.Height ?? 0;
+ video.Width = videoStream == null ? 0 : videoStream.Width ?? 0;
+
video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
@@ -370,9 +361,9 @@ namespace MediaBrowser.Providers.MediaInfo
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres))
{
- if (video.Genres.Count == 0 || isFullRefresh)
+ if (video.Genres.Length == 0 || isFullRefresh)
{
- video.Genres.Clear();
+ video.Genres = Array.Empty<string>();
foreach (var genre in data.Genres)
{
@@ -423,7 +414,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles)
{
// Don't use the embedded name for extras because it will often be the same name as the movie
- if (!video.ExtraType.HasValue && !video.IsOwnedItem)
+ if (!video.ExtraType.HasValue)
{
video.Name = data.Name;
}
@@ -498,19 +489,46 @@ namespace MediaBrowser.Providers.MediaInfo
var subtitleOptions = GetOptions();
- if (enableSubtitleDownloading && (subtitleOptions.DownloadEpisodeSubtitles &&
+ var libraryOptions = _libraryManager.GetLibraryOptions(video);
+
+ string[] subtitleDownloadLanguages;
+ bool SkipIfEmbeddedSubtitlesPresent;
+ bool SkipIfAudioTrackMatches;
+ bool RequirePerfectMatch;
+ bool enabled;
+
+ if (libraryOptions.SubtitleDownloadLanguages == null)
+ {
+ subtitleDownloadLanguages = subtitleOptions.DownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = subtitleOptions.SkipIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = subtitleOptions.SkipIfAudioTrackMatches;
+ RequirePerfectMatch = subtitleOptions.RequirePerfectMatch;
+ enabled = (subtitleOptions.DownloadEpisodeSubtitles &&
video is Episode) ||
(subtitleOptions.DownloadMovieSubtitles &&
- video is Movie))
+ video is Movie);
+ }
+ else
+ {
+ subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
+ RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
+ enabled = true;
+ }
+
+ if (enableSubtitleDownloading && enabled)
{
var downloadedLanguages = await new SubtitleDownloader(_logger,
_subtitleManager)
.DownloadSubtitles(video,
currentStreams.Concat(externalSubtitleStreams).ToList(),
- subtitleOptions.SkipIfEmbeddedSubtitlesPresent,
- subtitleOptions.SkipIfAudioTrackMatches,
- subtitleOptions.RequirePerfectMatch,
- subtitleOptions.DownloadLanguages,
+ SkipIfEmbeddedSubtitlesPresent,
+ SkipIfAudioTrackMatches,
+ RequirePerfectMatch,
+ subtitleDownloadLanguages,
+ libraryOptions.DisabledSubtitleFetchers,
+ libraryOptions.SubtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
// Rescan
@@ -520,7 +538,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToArray();
+ video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).ToArray();
currentStreams.AddRange(externalSubtitleStreams);
}
@@ -565,9 +583,9 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- private string[] FetchFromDvdLib(Video item, IIsoMount mount)
+ private string[] FetchFromDvdLib(Video item)
{
- var path = mount == null ? item.Path : mount.MountedPath;
+ var path = item.Path;
var dvd = new Dvd(path, _fileSystem);
var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
@@ -580,7 +598,7 @@ namespace MediaBrowser.Providers.MediaInfo
item.RunTimeTicks = GetRuntime(primaryTitle);
}
- return _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, mount, titleNumber)
+ return _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, null, titleNumber)
.Select(Path.GetFileName)
.ToArray();
}
@@ -592,43 +610,5 @@ namespace MediaBrowser.Providers.MediaInfo
.Select(i => i.Ticks)
.Sum();
}
-
- /// <summary>
- /// Mounts the iso if needed.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IsoMount.</returns>
- protected Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken)
- {
- if (item.VideoType == VideoType.Iso)
- {
- return _isoManager.Mount(item.Path, cancellationToken);
- }
-
- return Task.FromResult<IIsoMount>(null);
- }
-
- /// <summary>
- /// Determines the type of the iso.
- /// </summary>
- /// <param name="isoMount">The iso mount.</param>
- /// <returns>System.Nullable{IsoType}.</returns>
- private IsoType? DetermineIsoType(IIsoMount isoMount)
- {
- var fileSystemEntries = _fileSystem.GetFileSystemEntryPaths(isoMount.MountedPath).Select(Path.GetFileName).ToList();
-
- if (fileSystemEntries.Contains("video_ts", StringComparer.OrdinalIgnoreCase) ||
- fileSystemEntries.Contains("VIDEO_TS.IFO", StringComparer.OrdinalIgnoreCase))
- {
- return IsoType.Dvd;
- }
- if (fileSystemEntries.Contains("bdmv", StringComparer.OrdinalIgnoreCase))
- {
- return IsoType.BluRay;
- }
-
- return null;
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
index 7c6b55d3a..6b396967e 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -31,6 +31,8 @@ namespace MediaBrowser.Providers.MediaInfo
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
IEnumerable<string> languages,
+ string[] disabledSubtitleFetchers,
+ string[] subtitleFetcherOrder,
CancellationToken cancellationToken)
{
var downloadedLanguages = new List<string>();
@@ -38,7 +40,7 @@ namespace MediaBrowser.Providers.MediaInfo
foreach (var lang in languages)
{
var downloaded = await DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent,
- skipIfAudioTrackMatches, requirePerfectMatch, lang, cancellationToken).ConfigureAwait(false);
+ skipIfAudioTrackMatches, requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, cancellationToken).ConfigureAwait(false);
if (downloaded)
{
@@ -55,10 +57,11 @@ namespace MediaBrowser.Providers.MediaInfo
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
string lang,
+ string[] disabledSubtitleFetchers,
+ string[] subtitleFetcherOrder,
CancellationToken cancellationToken)
{
- if (video.LocationType != LocationType.FileSystem ||
- video.VideoType != VideoType.VideoFile)
+ if (video.VideoType != VideoType.VideoFile)
{
return Task.FromResult(false);
}
@@ -85,7 +88,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
return DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, skipIfAudioTrackMatches,
- requirePerfectMatch, lang, mediaType, cancellationToken);
+ requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, mediaType, cancellationToken);
}
private async Task<bool> DownloadSubtitles(Video video,
@@ -94,6 +97,8 @@ namespace MediaBrowser.Providers.MediaInfo
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
string language,
+ string[] disabledSubtitleFetchers,
+ string[] subtitleFetcherOrder,
VideoContentType mediaType,
CancellationToken cancellationToken)
{
@@ -140,7 +145,9 @@ namespace MediaBrowser.Providers.MediaInfo
// Stop as soon as we find something
SearchAllProviders = false,
- IsPerfectMatch = requirePerfectMatch
+ IsPerfectMatch = requirePerfectMatch,
+ DisabledSubtitleFetchers = disabledSubtitleFetchers,
+ SubtitleFetcherOrder = subtitleFetcherOrder
};
var episode = video as Episode;
@@ -159,8 +166,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (result != null)
{
- await _subtitleManager.DownloadSubtitles(video, result.Id, cancellationToken)
- .ConfigureAwait(false);
+ await _subtitleManager.DownloadSubtitles(video, result.Id, cancellationToken).ConfigureAwait(false);
return true;
}
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index 4e264bd22..a06509b56 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -40,13 +40,18 @@ namespace MediaBrowser.Providers.MediaInfo
{
var streams = new List<MediaStream>();
- GetExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache);
+ if (!video.IsFileProtocol)
+ {
+ return streams;
+ }
+
+ AddExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache);
startIndex += streams.Count;
try
{
- GetExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache);
+ AddExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache);
}
catch (IOException)
{
@@ -60,10 +65,15 @@ namespace MediaBrowser.Providers.MediaInfo
IDirectoryService directoryService,
bool clearCache)
{
- var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache);
-
var list = new List<string>();
+ if (!video.IsFileProtocol)
+ {
+ return list;
+ }
+
+ var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache);
+
foreach (var stream in streams)
{
list.Add(stream.Path);
@@ -72,17 +82,25 @@ namespace MediaBrowser.Providers.MediaInfo
return list;
}
- private void GetExternalSubtitleStreams(List<MediaStream> streams, string folder,
+ private void AddExternalSubtitleStreams(List<MediaStream> streams, string folder,
string videoPath,
int startIndex,
IDirectoryService directoryService,
bool clearCache)
{
+ var files = directoryService.GetFilePaths(folder, clearCache).OrderBy(i => i).ToArray();
+
+ AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
+ }
+
+ public void AddExternalSubtitleStreams(List<MediaStream> streams,
+ string videoPath,
+ int startIndex,
+ string[] files)
+ {
var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(videoPath);
videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoFileNameWithoutExtension);
- var files = directoryService.GetFilePaths(folder, clearCache);
-
foreach (var fullName in files)
{
var extension = Path.GetExtension(fullName);
@@ -131,16 +149,13 @@ namespace MediaBrowser.Providers.MediaInfo
var language = fileNameWithoutExtension
.Replace(".forced", string.Empty, StringComparison.OrdinalIgnoreCase)
.Replace(".foreign", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace(".default", string.Empty, StringComparison.OrdinalIgnoreCase)
.Split('.')
.LastOrDefault();
// Try to translate to three character code
// Be flexible and check against both the full and three character versions
- var culture = _localization.GetCultures()
- .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
+ var culture = _localization.FindLanguageInfo(language);
if (culture != null)
{
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 4d68735b9..6b4181dfb 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -64,56 +64,69 @@ namespace MediaBrowser.Providers.MediaInfo
{
var options = GetOptions();
- var types = new List<string>();
-
- if (options.DownloadEpisodeSubtitles)
- {
- types.Add("Episode");
- }
- if (options.DownloadMovieSubtitles)
- {
- types.Add("Movie");
- }
-
- if (types.Count == 0)
- {
- return;
- }
+ var types = new[] { "Episode", "Movie" };
var dict = new Dictionary<Guid, BaseItem>();
- foreach (var lang in options.DownloadLanguages)
+ foreach (var library in _libraryManager.RootFolder.Children.ToList())
{
- var query = new InternalItemsQuery
- {
- MediaTypes = new string[] {MediaType.Video},
- IsVirtualItem = false,
- IncludeItemTypes = types.ToArray(types.Count),
- DtoOptions = new DtoOptions(true),
- SourceTypes = new[] {SourceType.Library}
- };
-
- if (options.SkipIfAudioTrackMatches)
- {
- query.HasNoAudioTrackWithLanguage = lang;
- }
+ var libraryOptions = _libraryManager.GetLibraryOptions(library);
- if (options.SkipIfEmbeddedSubtitlesPresent)
+ string[] subtitleDownloadLanguages;
+ bool SkipIfEmbeddedSubtitlesPresent;
+ bool SkipIfAudioTrackMatches;
+ bool RequirePerfectMatch;
+
+ if (libraryOptions.SubtitleDownloadLanguages == null)
{
- // Exclude if it already has any subtitles of the same language
- query.HasNoSubtitleTrackWithLanguage = lang;
+ subtitleDownloadLanguages = options.DownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
+ RequirePerfectMatch = options.RequirePerfectMatch;
}
else
{
- // Exclude if it already has external subtitles of the same language
- query.HasNoExternalSubtitleTrackWithLanguage = lang;
+ subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
+ RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
}
- var videosByLanguage = _libraryManager.GetItemList(query);
-
- foreach (var video in videosByLanguage)
+ foreach (var lang in subtitleDownloadLanguages)
{
- dict[video.Id] = video;
+ var query = new InternalItemsQuery
+ {
+ MediaTypes = new string[] { MediaType.Video },
+ IsVirtualItem = false,
+ IncludeItemTypes = types,
+ DtoOptions = new DtoOptions(true),
+ SourceTypes = new[] { SourceType.Library },
+ Parent = library,
+ Recursive = true
+ };
+
+ if (SkipIfAudioTrackMatches)
+ {
+ query.HasNoAudioTrackWithLanguage = lang;
+ }
+
+ if (SkipIfEmbeddedSubtitlesPresent)
+ {
+ // Exclude if it already has any subtitles of the same language
+ query.HasNoSubtitleTrackWithLanguage = lang;
+ }
+ else
+ {
+ // Exclude if it already has external subtitles of the same language
+ query.HasNoExternalSubtitleTrackWithLanguage = lang;
+ }
+
+ var videosByLanguage = _libraryManager.GetItemList(query);
+
+ foreach (var video in videosByLanguage)
+ {
+ dict[video.Id] = video;
+ }
}
}
@@ -149,34 +162,50 @@ namespace MediaBrowser.Providers.MediaInfo
private async Task<bool> DownloadSubtitles(Video video, SubtitleOptions options, CancellationToken cancellationToken)
{
- if ((options.DownloadEpisodeSubtitles &&
- video is Episode) ||
- (options.DownloadMovieSubtitles &&
- video is Movie))
+ var mediaStreams = video.GetMediaStreams();
+
+ var libraryOptions = _libraryManager.GetLibraryOptions(video);
+
+ string[] subtitleDownloadLanguages;
+ bool SkipIfEmbeddedSubtitlesPresent;
+ bool SkipIfAudioTrackMatches;
+ bool RequirePerfectMatch;
+
+ if (libraryOptions.SubtitleDownloadLanguages == null)
{
- var mediaStreams = _mediaSourceManager.GetStaticMediaSources(video, false).First().MediaStreams;
-
- var downloadedLanguages = await new SubtitleDownloader(_logger,
- _subtitleManager)
- .DownloadSubtitles(video,
- mediaStreams,
- options.SkipIfEmbeddedSubtitlesPresent,
- options.SkipIfAudioTrackMatches,
- options.RequirePerfectMatch,
- options.DownloadLanguages,
- cancellationToken).ConfigureAwait(false);
-
- // Rescan
- if (downloadedLanguages.Count > 0)
- {
- await video.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- return false;
- }
+ subtitleDownloadLanguages = options.DownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
+ RequirePerfectMatch = options.RequirePerfectMatch;
+ }
+ else
+ {
+ subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
+ SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
+ SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
+ RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
+ }
- return true;
+ var downloadedLanguages = await new SubtitleDownloader(_logger,
+ _subtitleManager)
+ .DownloadSubtitles(video,
+ mediaStreams,
+ SkipIfEmbeddedSubtitlesPresent,
+ SkipIfAudioTrackMatches,
+ RequirePerfectMatch,
+ subtitleDownloadLanguages,
+ libraryOptions.DisabledSubtitleFetchers,
+ libraryOptions.SubtitleFetcherOrder,
+ cancellationToken).ConfigureAwait(false);
+
+ // Rescan
+ if (downloadedLanguages.Count > 0)
+ {
+ await video.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ return false;
}
- return false;
+ return true;
}
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 9fde9c70f..00ffe383f 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -29,12 +29,12 @@ namespace MediaBrowser.Providers.MediaInfo
_fileSystem = fileSystem;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType> { ImageType.Primary };
}
- public Task<DynamicImageResponse> GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken)
+ public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
var video = (Video)item;
@@ -62,11 +62,9 @@ namespace MediaBrowser.Providers.MediaInfo
public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationToken cancellationToken)
{
- var protocol = item.LocationType == LocationType.Remote
- ? MediaProtocol.Http
- : MediaProtocol.File;
+ var protocol = item.PathProtocol ?? MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, null, item.GetPlayableStreamFileNames());
+ var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, null, item.GetPlayableStreamFileNames(_mediaEncoder));
var mediaStreams =
item.GetMediaStreams();
@@ -129,11 +127,20 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Screen Grabber"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
+ if (item.IsShortcut)
+ {
+ return false;
+ }
+ if (!item.IsFileProtocol)
+ {
+ return false;
+ }
+
var video = item as Video;
- if (item.LocationType == LocationType.FileSystem && video != null && !video.IsPlaceHolder && !video.IsShortcut && video.IsCompleteMedia)
+ if (video != null && !video.IsPlaceHolder && video.IsCompleteMedia)
{
return true;
}
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index 545c3baba..6d489498e 100644
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -24,6 +24,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Providers.TV;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Movies
{
@@ -60,12 +61,12 @@ namespace MediaBrowser.Providers.Movies
get { return "FanArt"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Movie || item is BoxSet || item is MusicVideo;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -79,9 +80,9 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var baseItem = (BaseItem)item;
+ var baseItem = item;
var list = new List<RemoteImageInfo>();
var movieId = baseItem.GetProviderId(MetadataProviders.Tmdb);
@@ -165,7 +166,6 @@ namespace MediaBrowser.Providers.Movies
PopulateImages(list, obj.moviebackground, ImageType.Backdrop, 1920, 1080);
}
- private Regex _regex_http = new Regex("^http://");
private void PopulateImages(List<RemoteImageInfo> list, List<Image> images, ImageType type, int width, int height)
{
if (images == null)
@@ -189,11 +189,11 @@ namespace MediaBrowser.Providers.Movies
Width = width,
Height = height,
ProviderName = Name,
- Url = _regex_http.Replace(url, "https://", 1),
+ Url = url,
Language = i.lang
};
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
+ if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out likes))
{
info.CommunityRating = likes;
}
@@ -305,7 +305,6 @@ namespace MediaBrowser.Providers.Movies
}
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureMovieJson(string id, CancellationToken cancellationToken)
{
var path = GetFanartJsonPath(id);
@@ -314,9 +313,9 @@ namespace MediaBrowser.Providers.Movies
if (fileInfo.Exists)
{
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
index a000ed36e..8a409f9cf 100644
--- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
+++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
@@ -130,11 +130,10 @@ namespace MediaBrowser.Providers.Movies
movie.OriginalTitle = movieData.GetOriginalTitle();
- // Bug in Mono: WebUtility.HtmlDecode should return null if the string is null but in Mono it generate an System.ArgumentNullException.
- movie.Overview = movieData.overview != null ? WebUtility.HtmlDecode(movieData.overview) : null;
+ movie.Overview = string.IsNullOrWhiteSpace(movieData.overview) ? null : WebUtility.HtmlDecode(movieData.overview);
movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null;
- movie.HomePageUrl = movieData.homepage;
+ //movie.HomePageUrl = movieData.homepage;
if (!string.IsNullOrEmpty(movieData.tagline))
{
@@ -175,7 +174,6 @@ namespace MediaBrowser.Providers.Movies
//movie.VoteCount = movieData.vote_count;
- //release date and certification are retrieved based on configured country and we fall back on US if not there and to minimun release date if still no match
if (movieData.releases != null && movieData.releases.countries != null)
{
var releases = movieData.releases.countries.Where(i => !string.IsNullOrWhiteSpace(i.certification)).ToList();
@@ -226,7 +224,7 @@ namespace MediaBrowser.Providers.Movies
}
resultItem.ResetPeople();
- var tmdbImageUrl = settings.images.secure_base_url + "original";
+ var tmdbImageUrl = settings.images.GetImageUrl("original");
//Actors, Directors, Writers - all in People
//actors come from cast
@@ -307,19 +305,14 @@ namespace MediaBrowser.Providers.Movies
// movie.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
//}
- if (movieData.trailers != null && movieData.trailers.youtube != null &&
- movieData.trailers.youtube.Count > 0)
+ if (movieData.trailers != null && movieData.trailers.youtube != null)
{
- var hasTrailers = movie as IHasTrailers;
- if (hasTrailers != null)
+ movie.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
{
- hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
- {
- Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
- Name = i.name
+ Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
+ Name = i.name
- }).ToArray();
- }
+ }).ToArray();
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index 8dc4bd56a..ad45c8432 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Movie || item is MusicVideo || item is Trailer;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -55,13 +55,13 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
var language = item.GetPreferredMetadataLanguage();
- var results = await FetchImages((BaseItem)item, null, _jsonSerializer, cancellationToken).ConfigureAwait(false);
+ var results = await FetchImages(item, null, _jsonSerializer, cancellationToken).ConfigureAwait(false);
if (results == null)
{
@@ -70,7 +70,7 @@ namespace MediaBrowser.Providers.Movies
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var supportedImages = GetSupportedImages(item).ToList();
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index 06adbffd7..6266e717e 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Movies
var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var remoteResult = new RemoteSearchResult
{
@@ -156,15 +156,17 @@ namespace MediaBrowser.Providers.Movies
{
using (var json = response.Content)
{
- _tmdbSettings = _jsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json);
+ _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSettingsResult>(json).ConfigureAwait(false);
return _tmdbSettings;
}
}
}
- private const string TmdbConfigUrl = "https://api.themoviedb.org/3/configuration?api_key={0}";
- private const string GetMovieInfo3 = @"https://api.themoviedb.org/3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
+ public const string BaseMovieDbUrl = "https://api.themoviedb.org/";
+
+ private const string TmdbConfigUrl = BaseMovieDbUrl + "3/configuration?api_key={0}";
+ private const string GetMovieInfo3 = BaseMovieDbUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669";
internal static string AcceptHeader = "application/json,image/*";
@@ -209,7 +211,6 @@ namespace MediaBrowser.Providers.Movies
_jsonSerializer.SerializeToFile(mainResult, dataFilePath);
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureMovieInfo(string tmdbId, string language, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(tmdbId))
@@ -224,9 +225,9 @@ namespace MediaBrowser.Providers.Movies
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
@@ -354,7 +355,7 @@ namespace MediaBrowser.Providers.Movies
{
using (var json = response.Content)
{
- mainResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+ mainResult = await _jsonSerializer.DeserializeFromStreamAsync<CompleteMovieData>(json).ConfigureAwait(false);
}
}
}
@@ -399,7 +400,7 @@ namespace MediaBrowser.Providers.Movies
{
using (var json = response.Content)
{
- var englishResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+ var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<CompleteMovieData>(json).ConfigureAwait(false);
mainResult.overview = englishResult.overview;
}
@@ -638,7 +639,7 @@ namespace MediaBrowser.Providers.Movies
{
get
{
- return 0;
+ return 1;
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs
index 673af5534..3b4a385d4 100644
--- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbSearch.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Movies
public class MovieDbSearch
{
private static readonly CultureInfo EnUs = new CultureInfo("en-US");
- private const string Search3 = @"https://api.themoviedb.org/3/search/{3}?api_key={1}&query={0}&language={2}";
+ private const string Search3 = MovieDbProvider.BaseMovieDbUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669";
internal static string AcceptHeader = "application/json,image/*";
@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Movies
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
if (!string.IsNullOrWhiteSpace(name))
{
@@ -164,7 +164,7 @@ namespace MediaBrowser.Providers.Movies
{
using (var json = response.Content)
{
- var searchResults = _json.DeserializeFromStream<TmdbMovieSearchResults>(json);
+ var searchResults = await _json.DeserializeFromStreamAsync<TmdbMovieSearchResults>(json).ConfigureAwait(false);
var results = searchResults.results ?? new List<TmdbMovieSearchResult>();
@@ -219,7 +219,7 @@ namespace MediaBrowser.Providers.Movies
{
using (var json = response.Content)
{
- var searchResults = _json.DeserializeFromStream<TmdbTvSearchResults>(json);
+ var searchResults = await _json.DeserializeFromStreamAsync<TmdbTvSearchResults>(json).ConfigureAwait(false);
var results = searchResults.results ?? new List<TvResult>();
diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs
index a6b7bde6f..208d412f6 100644
--- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs
+++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs
@@ -9,6 +9,8 @@ namespace MediaBrowser.Providers.Movies
{
public class MovieDbMovieExternalId : IExternalId
{
+ public const string BaseMovieDbUrl = "https://www.themoviedb.org/";
+
public string Name
{
get { return "TheMovieDb"; }
@@ -21,7 +23,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "https://www.themoviedb.org/movie/{0}"; }
+ get { return BaseMovieDbUrl + "movie/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -51,7 +53,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "https://www.themoviedb.org/tv/{0}"; }
+ get { return MovieDbMovieExternalId.BaseMovieDbUrl + "tv/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -74,7 +76,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "https://www.themoviedb.org/collection/{0}"; }
+ get { return MovieDbMovieExternalId.BaseMovieDbUrl + "collection/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -97,7 +99,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "https://www.themoviedb.org/person/{0}"; }
+ get { return MovieDbMovieExternalId.BaseMovieDbUrl + "person/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -120,7 +122,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "https://www.themoviedb.org/collection/{0}"; }
+ get { return MovieDbMovieExternalId.BaseMovieDbUrl + "collection/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -143,7 +145,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "http://www.imdb.com/title/{0}"; }
+ get { return "https://www.imdb.com/title/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -174,7 +176,7 @@ namespace MediaBrowser.Providers.Movies
public string UrlFormatString
{
- get { return "http://www.imdb.com/name/{0}"; }
+ get { return "https://www.imdb.com/name/{0}"; }
}
public bool Supports(IHasProviderIds item)
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index dc3146f2a..f0674ac9b 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Movies
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- if (replaceData || target.Item.TrailerTypes.Count == 0)
+ if (replaceData || target.Item.TrailerTypes.Length == 0)
{
target.Item.TrailerTypes = source.Item.TrailerTypes;
}
diff --git a/MediaBrowser.Providers/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Movies/TmdbSettings.cs
index 12bb77afc..facc4a31b 100644
--- a/MediaBrowser.Providers/Movies/TmdbSettings.cs
+++ b/MediaBrowser.Providers/Movies/TmdbSettings.cs
@@ -8,6 +8,11 @@ namespace MediaBrowser.Providers.Movies
public string secure_base_url { get; set; }
public List<string> poster_sizes { get; set; }
public List<string> profile_sizes { get; set; }
+
+ public string GetImageUrl(string image)
+ {
+ return secure_base_url + image;
+ }
}
internal class TmdbSettingsResult
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index 48f520681..338fc1c6d 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -11,72 +11,69 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Providers.Music
{
public class AlbumMetadataService : MetadataService<MusicAlbum, AlbumInfo>
{
- protected override ItemUpdateType BeforeSaveInternal(MusicAlbum item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
{
- var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
+ return item.GetRecursiveChildren(i => i is Audio)
+ .ToList();
+ }
+
+ protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ {
+ var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
- if (!item.IsLocked)
+ if (!item.LockedFields.Contains(MetadataFields.Name))
{
- var songs = item.GetRecursiveChildren(i => i is Audio)
- .Cast<Audio>()
- .ToList();
+ var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
- if (!item.LockedFields.Contains(MetadataFields.Genres))
+ if (!string.IsNullOrEmpty(name))
{
- var currentList = item.Genres.ToList();
-
- item.Genres = songs.SelectMany(i => i.Genres)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ if (!string.Equals(item.Name, name, StringComparison.Ordinal))
{
+ item.Name = name;
updateType = updateType | ItemUpdateType.MetadataEdit;
}
}
+ }
- if (!item.LockedFields.Contains(MetadataFields.Studios))
- {
- var currentList = item.Studios;
-
- item.Studios = songs.SelectMany(i => i.Studios)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToArray();
+ var songs = children.Cast<Audio>().ToArray();
- if (currentList.Length != item.Studios.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
- }
+ updateType = updateType | SetAlbumArtistFromSongs(item, songs);
+ updateType = updateType | SetArtistsFromSongs(item, songs);
+ }
- if (!item.LockedFields.Contains(MetadataFields.Name))
- {
- var name = songs.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
+ return updateType;
+ }
- if (!string.IsNullOrEmpty(name))
- {
- if (!string.Equals(item.Name, name, StringComparison.Ordinal))
- {
- item.Name = name;
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
- }
- }
+ protected override bool EnableUpdatingPremiereDateFromChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
- updateType = updateType | SetAlbumArtistFromSongs(item, songs);
- updateType = updateType | SetArtistsFromSongs(item, songs);
- updateType = updateType | SetDateFromSongs(item, songs);
- }
+ protected override bool EnableUpdatingGenresFromChildren
+ {
+ get
+ {
+ return true;
}
+ }
- return updateType;
+ protected override bool EnableUpdatingStudiosFromChildren
+ {
+ get
+ {
+ return true;
+ }
}
private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IEnumerable<Audio> songs)
@@ -117,40 +114,6 @@ namespace MediaBrowser.Providers.Music
return updateType;
}
- private ItemUpdateType SetDateFromSongs(MusicAlbum item, List<Audio> songs)
- {
- var updateType = ItemUpdateType.None;
-
- var date = songs.Select(i => i.PremiereDate)
- .FirstOrDefault(i => i.HasValue);
-
- var originalPremiereDate = item.PremiereDate;
- var originalProductionYear = item.ProductionYear;
-
- if (date.HasValue)
- {
- item.PremiereDate = date.Value;
- item.ProductionYear = date.Value.Year;
- }
- else
- {
- var year = songs.Select(i => i.ProductionYear ?? 1800).FirstOrDefault(i => i != 1800);
-
- if (year != 1800)
- {
- item.ProductionYear = year;
- }
- }
-
- if ((originalPremiereDate ?? DateTime.MinValue) != (item.PremiereDate ?? DateTime.MinValue) ||
- (originalProductionYear ?? -1) != (item.ProductionYear ?? -1))
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
-
- return updateType;
- }
-
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 70ae52544..7741a3f43 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -10,41 +10,29 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Providers.Music
{
public class ArtistMetadataService : MetadataService<MusicArtist, ArtistInfo>
{
- protected override ItemUpdateType BeforeSaveInternal(MusicArtist item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
{
- var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
-
- if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
- {
- if (!item.IsLocked && !item.LockedFields.Contains(MetadataFields.Genres))
+ return item.IsAccessedByName ?
+ item.GetTaggedItems(new Controller.Entities.InternalItemsQuery
{
- var taggedItems = item.IsAccessedByName ?
- item.GetTaggedItems(new Controller.Entities.InternalItemsQuery()
- {
- Recursive = true,
- IsFolder = false
- }) :
- item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
-
- var currentList = item.Genres;
-
- item.Genres = taggedItems.SelectMany(i => i.Genres)
- .DistinctNames()
- .ToList();
+ Recursive = true,
+ IsFolder = false
+ }) :
+ item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
+ }
- if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
- }
+ protected override bool EnableUpdatingGenresFromChildren
+ {
+ get
+ {
+ return true;
}
-
- return updateType;
}
protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
index 0ce221b06..273962c6a 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(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -34,7 +34,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@@ -105,7 +105,7 @@ namespace MediaBrowser.Providers.Music
}
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is MusicAlbum;
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
index 1082685a8..aaf0ece3d 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
@@ -84,7 +84,7 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrEmpty(result.strGenre))
{
- item.Genres = new List<string> { result.strGenre };
+ item.Genres = new [] { result.strGenre };
}
item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
@@ -133,7 +133,6 @@ namespace MediaBrowser.Providers.Music
get { return "TheAudioDB"; }
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureInfo(string musicBrainzReleaseGroupId, CancellationToken cancellationToken)
{
var xmlPath = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
@@ -142,9 +141,9 @@ namespace MediaBrowser.Providers.Music
if (fileInfo.Exists)
{
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
index dded509e2..89635872f 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(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
@@ -138,7 +138,7 @@ namespace MediaBrowser.Providers.Music
get { return "TheAudioDB"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is MusicArtist;
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
index 66d688959..e0d193ff1 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music
public static AudioDbArtistProvider Current;
private const string ApiKey = "49jhsf8248yfahka89724011";
- public const string BaseUrl = "http://www.theaudiodb.com/api/v1/json/" + ApiKey;
+ public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey;
public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json)
{
@@ -71,11 +71,11 @@ namespace MediaBrowser.Providers.Music
private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage)
{
- item.HomePageUrl = result.strWebsite;
+ //item.HomePageUrl = result.strWebsite;
if (!string.IsNullOrEmpty(result.strGenre))
{
- item.Genres = new List<string> { result.strGenre };
+ item.Genres = new[] { result.strGenre };
}
item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
@@ -121,7 +121,6 @@ namespace MediaBrowser.Providers.Music
get { return "TheAudioDB"; }
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureArtistInfo(string musicBrainzId, CancellationToken cancellationToken)
{
var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
@@ -130,9 +129,9 @@ namespace MediaBrowser.Providers.Music
if (fileInfo.Exists)
{
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
diff --git a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs b/MediaBrowser.Providers/Music/AudioDbExternalIds.cs
index 885aa61d8..fcb7c338d 100644
--- a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs
+++ b/MediaBrowser.Providers/Music/AudioDbExternalIds.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString
{
- get { return "http://www.theaudiodb.com/album/{0}"; }
+ get { return "https://www.theaudiodb.com/album/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString
{
- get { return "http://www.theaudiodb.com/album/{0}"; }
+ get { return "https://www.theaudiodb.com/album/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString
{
- get { return "http://www.theaudiodb.com/artist/{0}"; }
+ get { return "https://www.theaudiodb.com/artist/{0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString
{
- get { return "http://www.theaudiodb.com/artist/{0}"; }
+ get { return "https://www.theaudiodb.com/artist/{0}"; }
}
public bool Supports(IHasProviderIds item)
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 7a6826d31..2f7d70be0 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -18,6 +18,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Music
{
@@ -47,12 +48,12 @@ namespace MediaBrowser.Providers.Music
get { return "FanArt"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is MusicAlbum;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -61,7 +62,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var album = (MusicAlbum)item;
@@ -153,7 +154,6 @@ namespace MediaBrowser.Providers.Music
}
}
- private Regex _regex_http = new Regex("^http://");
private void PopulateImages(List<RemoteImageInfo> list,
List<FanartArtistProvider.FanartArtistImage> images,
ImageType type,
@@ -181,11 +181,11 @@ namespace MediaBrowser.Providers.Music
Width = width,
Height = height,
ProviderName = Name,
- Url = _regex_http.Replace(url, "https://", 1),
+ Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
Language = i.lang
};
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
+ if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out likes))
{
info.CommunityRating = likes;
}
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index c06ee9d73..99a717ca1 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Music
{
@@ -58,12 +59,12 @@ namespace MediaBrowser.Providers.Music
get { return "FanArt"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is MusicArtist;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -75,7 +76,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var artist = (MusicArtist)item;
@@ -151,7 +152,6 @@ namespace MediaBrowser.Providers.Music
PopulateImages(list, obj.musicarts, ImageType.Art, 500, 281);
}
- private Regex _regex_http = new Regex("^http://");
private void PopulateImages(List<RemoteImageInfo> list,
List<FanartArtistImage> images,
ImageType type,
@@ -179,11 +179,11 @@ namespace MediaBrowser.Providers.Music
Width = width,
Height = height,
ProviderName = Name,
- Url = _regex_http.Replace(url, "https://", 1),
+ Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
Language = i.lang
};
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
+ if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out likes))
{
info.CommunityRating = likes;
}
@@ -209,7 +209,6 @@ namespace MediaBrowser.Providers.Music
});
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureArtistJson(string musicBrainzId, CancellationToken cancellationToken)
{
var jsonPath = GetArtistJsonPath(_config.ApplicationPaths, musicBrainzId);
@@ -218,9 +217,9 @@ namespace MediaBrowser.Providers.Music
if (fileInfo.Exists)
{
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index 3befca428..c676f9198 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -75,8 +75,11 @@ namespace MediaBrowser.Providers.Music
{
isNameSearch = true;
+ // I'm sure there is a better way but for now it resolves search for 12" Mixes
+ var queryName = searchInfo.Name.Replace("\"", string.Empty);
+
url = string.Format("/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
- WebUtility.UrlEncode(searchInfo.Name),
+ WebUtility.UrlEncode(queryName),
WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
}
}
@@ -296,7 +299,7 @@ namespace MediaBrowser.Providers.Music
public string Overview;
public int? Year;
- public List<Tuple<string, string>> Artists = new List<Tuple<string, string>>();
+ public List<ValueTuple<string, string>> Artists = new List<ValueTuple<string, string>>();
public static List<ReleaseResult> Parse(XmlReader reader)
{
@@ -450,7 +453,7 @@ namespace MediaBrowser.Providers.Music
{
var artist = ParseArtistCredit(subReader);
- if (artist != null)
+ if (!string.IsNullOrEmpty(artist.Item1))
{
result.Artists.Add(artist);
}
@@ -475,7 +478,7 @@ namespace MediaBrowser.Providers.Music
}
}
- private static Tuple<string, string> ParseArtistCredit(XmlReader reader)
+ private static ValueTuple<string, string> ParseArtistCredit(XmlReader reader)
{
reader.MoveToContent();
reader.Read();
@@ -509,10 +512,10 @@ namespace MediaBrowser.Providers.Music
}
}
- return null;
+ return new ValueTuple<string, string>();
}
- private static Tuple<string, string> ParseArtistNameCredit(XmlReader reader)
+ private static ValueTuple<string, string> ParseArtistNameCredit(XmlReader reader)
{
reader.MoveToContent();
reader.Read();
@@ -549,15 +552,10 @@ namespace MediaBrowser.Providers.Music
}
}
- if (string.IsNullOrWhiteSpace(name))
- {
- return null;
- }
-
- return new Tuple<string, string>(name, null);
+ return new ValueTuple<string, string>(name, null);
}
- private static Tuple<string, string> ParseArtistArtistCredit(XmlReader reader, string artistId)
+ private static ValueTuple<string, string> ParseArtistArtistCredit(XmlReader reader, string artistId)
{
reader.MoveToContent();
reader.Read();
@@ -591,12 +589,7 @@ namespace MediaBrowser.Providers.Music
}
}
- if (string.IsNullOrWhiteSpace(name))
- {
- return null;
- }
-
- return new Tuple<string, string>(name, artistId);
+ return new ValueTuple<string, string>(name, artistId);
}
private async Task<string> GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken)
@@ -753,45 +746,43 @@ namespace MediaBrowser.Providers.Music
private async Task<List<MbzUrl>> RefreshMzbUrls(bool forceMusicBrainzProper = false)
{
- List<MbzUrl> list;
+ List<MbzUrl> list = null;
- if (forceMusicBrainzProper)
- {
- list = new List<MbzUrl>
- {
- new MbzUrl
- {
- url = MusicBrainzBaseUrl,
- throttleMs = 1000
- }
- };
- }
- else
+ if (!forceMusicBrainzProper)
{
- try
+ var musicbrainzadminurl = _appHost.GetValue("musicbrainzadminurl");
+
+ if (!string.IsNullOrEmpty(musicbrainzadminurl))
{
- var options = new HttpRequestOptions
+ try
{
- Url = "https://mb3admin.com/admin/service/standards/musicBrainzUrls",
- UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion
- };
+ var options = new HttpRequestOptions
+ {
+ Url = musicbrainzadminurl,
+ UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion
+ };
- using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
- {
- using (var stream = response.Content)
+ using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
{
- var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
+ using (var stream = response.Content)
+ {
+ var results = await _json.DeserializeFromStreamAsync<List<MbzUrl>>(stream).ConfigureAwait(false);
- list = results;
+ list = results;
+ }
}
+ _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting music brainz info", ex);
}
- _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
}
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting music brainz info", ex);
+ }
- list = new List<MbzUrl>
+ if (list == null)
+ {
+ list = new List<MbzUrl>
{
new MbzUrl
{
@@ -799,7 +790,6 @@ namespace MediaBrowser.Providers.Music
throttleMs = 1000
}
};
- }
}
_mbzUrls = list.ToList();
diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
index f514791a7..8e2a47100 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Music
// They seem to throw bad request failures on any term with a slash
var nameToSearch = searchInfo.Name.Replace('/', ' ');
- var url = String.Format("/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch));
+ var url = String.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
{
diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs
index 814488df1..4d026ea0f 100644
--- a/MediaBrowser.Providers/Music/MusicExternalIds.cs
+++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Providers.Music
{
@@ -141,4 +142,27 @@ namespace MediaBrowser.Providers.Music
return item is Audio;
}
}
+
+ public class ImvdbId : IExternalId
+ {
+ public string Name
+ {
+ get { return "IMVDb"; }
+ }
+
+ public string Key
+ {
+ get { return "IMVDb"; }
+ }
+
+ public string UrlFormatString
+ {
+ get { return null; }
+ }
+
+ public bool Supports(IHasProviderIds item)
+ {
+ return item is MusicVideo;
+ }
+ }
}
diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
index 1ef420ddd..1eb9b3103 100644
--- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
@@ -12,8 +12,7 @@ using MediaBrowser.Model.Serialization;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
+using MediaBrowser.Common;
namespace MediaBrowser.Providers.Omdb
{
@@ -23,16 +22,18 @@ namespace MediaBrowser.Providers.Omdb
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
+ private readonly IApplicationHost _appHost;
- public OmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+ public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
+ _appHost = appHost;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -40,13 +41,13 @@ namespace MediaBrowser.Providers.Omdb
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
var list = new List<RemoteImageInfo>();
- var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager);
+ var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager);
if (!string.IsNullOrWhiteSpace(imdbId))
{
@@ -56,7 +57,7 @@ namespace MediaBrowser.Providers.Omdb
{
if (item is Episode)
{
- // img.omdbapi.com returning 404's
+ // img.omdbapi.com is returning 404's
list.Add(new RemoteImageInfo
{
ProviderName = Name,
@@ -91,7 +92,7 @@ namespace MediaBrowser.Providers.Omdb
get { return "The Open Movie Database"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Movie || item is Trailer || item is Episode;
}
diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
index c1b98dfbf..8c7ef71b6 100644
--- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
@@ -18,6 +18,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common;
namespace MediaBrowser.Providers.Omdb
{
@@ -30,8 +31,9 @@ namespace MediaBrowser.Providers.Omdb
private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
+ private readonly IApplicationHost _appHost;
- public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+ public OmdbItemProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
@@ -39,6 +41,7 @@ namespace MediaBrowser.Providers.Omdb
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
+ _appHost = appHost;
}
public int Order
@@ -46,7 +49,7 @@ namespace MediaBrowser.Providers.Omdb
get
{
// After primary option
- return 1;
+ return 2;
}
}
@@ -124,7 +127,7 @@ namespace MediaBrowser.Providers.Omdb
}
}
- var url = OmdbProvider.GetOmdbUrl(urlQuery, cancellationToken);
+ var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken);
using (var response = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
@@ -134,7 +137,7 @@ namespace MediaBrowser.Providers.Omdb
if (isSearch)
{
- var searchResultList = _jsonSerializer.DeserializeFromStream<SearchResultList>(stream);
+ var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false);
if (searchResultList != null && searchResultList.Search != null)
{
resultList.AddRange(searchResultList.Search);
@@ -142,7 +145,7 @@ namespace MediaBrowser.Providers.Omdb
}
else
{
- var result = _jsonSerializer.DeserializeFromStream<SearchResult>(stream);
+ var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false);
if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
{
resultList.Add(result);
@@ -168,7 +171,7 @@ namespace MediaBrowser.Providers.Omdb
int parsedYear;
if (result.Year.Length > 0
- && int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Any, CultureInfo.InvariantCulture, out parsedYear))
+ && int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedYear))
{
item.ProductionYear = parsedYear;
}
@@ -226,7 +229,7 @@ namespace MediaBrowser.Providers.Omdb
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.HasMetadata = true;
- await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
@@ -258,7 +261,7 @@ namespace MediaBrowser.Providers.Omdb
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.HasMetadata = true;
- await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
index 2cac44944..5c4eb62a8 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
@@ -14,6 +14,7 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common;
namespace MediaBrowser.Providers.Omdb
{
@@ -24,13 +25,15 @@ namespace MediaBrowser.Providers.Omdb
private readonly IServerConfigurationManager _configurationManager;
private readonly IHttpClient _httpClient;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+ private readonly IApplicationHost _appHost;
- public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+ public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
+ _appHost = appHost;
}
public async Task Fetch<T>(MetadataResult<T> itemResult, string imdbId, string language, string country, CancellationToken cancellationToken)
@@ -90,10 +93,10 @@ namespace MediaBrowser.Providers.Omdb
item.CommunityRating = imdbRating;
}
- if (!string.IsNullOrEmpty(result.Website))
- {
- item.HomePageUrl = result.Website;
- }
+ //if (!string.IsNullOrEmpty(result.Website))
+ //{
+ // item.HomePageUrl = result.Website;
+ //}
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
@@ -197,10 +200,10 @@ namespace MediaBrowser.Providers.Omdb
item.CommunityRating = imdbRating;
}
- if (!string.IsNullOrEmpty(result.Website))
- {
- item.HomePageUrl = result.Website;
- }
+ //if (!string.IsNullOrEmpty(result.Website))
+ //{
+ // item.HomePageUrl = result.Website;
+ //}
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
@@ -265,9 +268,16 @@ namespace MediaBrowser.Providers.Omdb
return false;
}
- public static string GetOmdbUrl(string query, CancellationToken cancellationToken)
+ public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken)
{
- var url = "https://www.omdbapi.com?apikey=fe53f97e";
+ var baseUrl = appHost.GetValue("omdb_baseurl");
+
+ if (string.IsNullOrEmpty(baseUrl))
+ {
+ baseUrl = "https://www.omdbapi.com";
+ }
+
+ var url = baseUrl + "?apikey=fe53f97e";
if (!string.IsNullOrWhiteSpace(query))
{
@@ -293,19 +303,19 @@ namespace MediaBrowser.Providers.Omdb
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 1)
{
return path;
}
}
- var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), cancellationToken);
+ var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken);
using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
using (var stream = response.Content)
{
- var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
+ var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
@@ -330,19 +340,19 @@ namespace MediaBrowser.Providers.Omdb
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 1)
{
return path;
}
}
- var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), cancellationToken);
+ var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken);
using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
using (var stream = response.Content)
{
- var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream);
+ var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
@@ -401,7 +411,7 @@ namespace MediaBrowser.Providers.Omdb
// But only do it if english is the preferred language because this data will not be localized
if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre))
{
- item.Genres.Clear();
+ item.Genres = Array.Empty<string>();
foreach (var genre in result.Genre
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
index 4dfcdba09..d4c8289e5 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Person;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.People
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var person = (Person)item;
var id = person.GetProviderId(MetadataProviders.Tmdb);
@@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.People
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
return GetImages(images, item.GetPreferredMetadataLanguage(), tmdbImageUrl);
}
diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
index c921df61c..7af058bcd 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
@@ -57,7 +57,7 @@ namespace MediaBrowser.Providers.People
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -89,7 +89,7 @@ namespace MediaBrowser.Providers.People
return new List<RemoteSearchResult>();
}
- var url = string.Format(@"https://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey);
+ var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey);
using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
@@ -101,7 +101,7 @@ namespace MediaBrowser.Providers.People
{
using (var json = response.Content)
{
- var result = _jsonSerializer.DeserializeFromStream<PersonSearchResults>(json) ??
+ var result = await _jsonSerializer.DeserializeFromStreamAsync<PersonSearchResults>(json).ConfigureAwait(false) ??
new PersonSearchResults();
return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl));
@@ -164,7 +164,7 @@ namespace MediaBrowser.Providers.People
// TODO: This should go in PersonMetadataService, not each person provider
item.Name = id.Name;
- item.HomePageUrl = info.homepage;
+ //item.HomePageUrl = info.homepage;
if (!string.IsNullOrWhiteSpace(info.place_of_birth))
{
@@ -219,12 +219,12 @@ namespace MediaBrowser.Providers.People
var fileInfo = _fileSystem.GetFileSystemInfo(dataFilePath);
- if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
return;
}
- var url = string.Format(@"https://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id);
+ var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id);
using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
index 2c3ba00c8..13cdc79fb 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Person;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -61,12 +61,12 @@ namespace MediaBrowser.Providers.People
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Series).Name },
- PersonIds = new[] { item.Id.ToString("N") },
+ PersonIds = new[] { item.Id },
DtoOptions = new DtoOptions(false)
{
EnableImages = false
diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
new file mode 100644
index 000000000..da4873ca4
--- /dev/null
+++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
@@ -0,0 +1,189 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Chapters;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Playlists;
+using System.IO;
+using PlaylistsNET;
+using PlaylistsNET.Content;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Playlists
+{
+ public class PlaylistItemsProvider : ICustomMetadataProvider<Playlist>,
+ IHasOrder,
+ IForcedProvider,
+ IPreRefreshProvider,
+ IHasItemChangeMonitor
+ {
+ private ILogger _logger;
+ private IFileSystem _fileSystem;
+
+ public PlaylistItemsProvider(IFileSystem fileSystem, ILogger logger)
+ {
+ _fileSystem = fileSystem;
+ _logger = logger;
+ }
+
+ public string Name
+ {
+ get { return "Playlist Reader"; }
+ }
+
+ public Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ {
+ var path = item.Path;
+ if (!Playlist.IsPlaylistFile(path))
+ {
+ return Task.FromResult(ItemUpdateType.None);
+ }
+
+ var extension = Path.GetExtension(path);
+ if (!Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return Task.FromResult(ItemUpdateType.None);
+ }
+
+ using (var stream = _fileSystem.OpenRead(path))
+ {
+ var items = GetItems(stream, extension).ToArray();
+
+ item.LinkedChildren = items;
+ }
+
+ return Task.FromResult(ItemUpdateType.None);
+ }
+
+ private IEnumerable<LinkedChild> GetItems(Stream stream, string extension)
+ {
+ if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetWplItems(stream);
+ }
+ if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetZplItems(stream);
+ }
+ if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetM3uItems(stream);
+ }
+ if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetM3u8Items(stream);
+ }
+ if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetPlsItems(stream);
+ }
+
+ return new List<LinkedChild>();
+ }
+
+ private IEnumerable<LinkedChild> GetPlsItems(Stream stream)
+ {
+ var content = new PlsContent();
+ var playlist = content.GetFromStream(stream);
+
+ return playlist.PlaylistEntries.Select(i => new LinkedChild
+ {
+ Path = i.Path,
+ Type = LinkedChildType.Manual
+ });
+ }
+
+ private IEnumerable<LinkedChild> GetM3u8Items(Stream stream)
+ {
+ var content = new M3u8Content();
+ var playlist = content.GetFromStream(stream);
+
+ return playlist.PlaylistEntries.Select(i => new LinkedChild
+ {
+ Path = i.Path,
+ Type = LinkedChildType.Manual
+ });
+ }
+
+ private IEnumerable<LinkedChild> GetM3uItems(Stream stream)
+ {
+ var content = new M3uContent();
+ var playlist = content.GetFromStream(stream);
+
+ return playlist.PlaylistEntries.Select(i => new LinkedChild
+ {
+ Path = i.Path,
+ Type = LinkedChildType.Manual
+ });
+ }
+
+ private IEnumerable<LinkedChild> GetZplItems(Stream stream)
+ {
+ var content = new ZplContent();
+ var playlist = content.GetFromStream(stream);
+
+ return playlist.PlaylistEntries.Select(i => new LinkedChild
+ {
+ Path = i.Path,
+ Type = LinkedChildType.Manual
+ });
+ }
+
+ private IEnumerable<LinkedChild> GetWplItems(Stream stream)
+ {
+ WplContent content = new WplContent();
+ var playlist = content.GetFromStream(stream);
+
+ return playlist.PlaylistEntries.Select(i => new LinkedChild
+ {
+ Path = i.Path,
+ Type = LinkedChildType.Manual
+ });
+ }
+
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
+ {
+ var path = item.Path;
+
+ if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
+ {
+ var file = directoryService.GetFile(path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ _logger.Debug("Refreshing {0} due to date modified timestamp change.", path);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public int Order
+ {
+ get
+ {
+ // Run last
+ return 100;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index a81facfb5..1d6591792 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -10,11 +10,17 @@ using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Providers.Playlists
{
class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
{
+ protected override IList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
+ {
+ return item.GetLinkedChildren();
+ }
+
protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@@ -22,44 +28,40 @@ namespace MediaBrowser.Providers.Playlists
var sourceItem = source.Item;
var targetItem = target.Item;
- if (replaceData || string.IsNullOrEmpty(targetItem.PlaylistMediaType))
- {
- targetItem.PlaylistMediaType = sourceItem.PlaylistMediaType;
- }
-
if (mergeMetadataSettings)
{
+ targetItem.PlaylistMediaType = sourceItem.PlaylistMediaType;
targetItem.LinkedChildren = sourceItem.LinkedChildren;
targetItem.Shares = sourceItem.Shares;
}
}
- protected override ItemUpdateType BeforeSaveInternal(Playlist item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
- var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
+ }
- if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
+ protected override bool EnableUpdatingGenresFromChildren
+ {
+ get
{
- if (!item.IsLocked && !item.LockedFields.Contains(MetadataFields.Genres))
- {
- var currentList = item.Genres;
-
- item.Genres = item.GetLinkedChildren().SelectMany(i => i.Genres)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
- }
+ return true;
}
+ }
- return updateType;
+ protected override bool EnableUpdatingOfficialRatingFromChildren
+ {
+ get
+ {
+ return true;
+ }
}
- public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
+ protected override bool EnableUpdatingStudiosFromChildren
{
+ get
+ {
+ return true;
+ }
}
}
}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index f63615430..2949c903f 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -4,13 +4,14 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.ImagesByName;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using System;
+using MediaBrowser.Common.Progress;
namespace MediaBrowser.Providers.Studios
{
@@ -29,20 +30,15 @@ namespace MediaBrowser.Providers.Studios
public string Name
{
- get { return ProviderName; }
- }
-
- public static string ProviderName
- {
get { return "Emby Designs"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Studio;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -51,12 +47,12 @@ namespace MediaBrowser.Providers.Studios
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, bool posters, bool thumbs, CancellationToken cancellationToken)
+ private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, bool posters, bool thumbs, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -83,11 +79,11 @@ namespace MediaBrowser.Providers.Studios
return list.Where(i => i != null);
}
- private RemoteImageInfo GetImage(IHasMetadata item, string filename, ImageType type, string remoteFilename)
+ private RemoteImageInfo GetImage(BaseItem item, string filename, ImageType type, string remoteFilename)
{
- var list = ImageUtils.GetAvailableImages(filename, _fileSystem);
+ var list = GetAvailableImages(filename, _fileSystem);
- var match = ImageUtils.FindMatch(item, list);
+ var match = FindMatch(item, list);
if (!string.IsNullOrEmpty(match))
{
@@ -113,14 +109,14 @@ namespace MediaBrowser.Providers.Studios
{
const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studiothumbs.txt";
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, cancellationToken);
+ return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken);
}
private Task<string> EnsurePosterList(string file, CancellationToken cancellationToken)
{
const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studioposters.txt";
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, cancellationToken);
+ return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken);
}
public int Order
@@ -137,5 +133,86 @@ namespace MediaBrowser.Providers.Studios
BufferContent = false
});
}
+
+ /// <summary>
+ /// Ensures the list.
+ /// </summary>
+ /// <param name="url">The URL.</param>
+ /// <param name="file">The file.</param>
+ /// <param name="httpClient">The HTTP client.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ public async Task<string> EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, CancellationToken cancellationToken)
+ {
+ var fileInfo = fileSystem.GetFileInfo(file);
+
+ if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
+ {
+ var temp = await httpClient.GetTempFile(new HttpRequestOptions
+ {
+ CancellationToken = cancellationToken,
+ Progress = new SimpleProgress<double>(),
+ Url = url
+
+ }).ConfigureAwait(false);
+
+ fileSystem.CreateDirectory(fileSystem.GetDirectoryName(file));
+
+ try
+ {
+ fileSystem.CopyFile(temp, file, true);
+ }
+ catch
+ {
+
+ }
+
+ return temp;
+ }
+
+ return file;
+ }
+
+ public string FindMatch(BaseItem item, IEnumerable<string> images)
+ {
+ var name = GetComparableName(item.Name);
+
+ return images.FirstOrDefault(i => string.Equals(name, GetComparableName(i), StringComparison.OrdinalIgnoreCase));
+ }
+
+ private string GetComparableName(string name)
+ {
+ return name.Replace(" ", string.Empty)
+ .Replace(".", string.Empty)
+ .Replace("&", string.Empty)
+ .Replace("!", string.Empty)
+ .Replace(",", string.Empty)
+ .Replace("/", string.Empty);
+ }
+
+ public IEnumerable<string> GetAvailableImages(string file, IFileSystem fileSystem)
+ {
+ using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ {
+ using (var reader = new StreamReader(fileStream))
+ {
+ var lines = new List<string>();
+
+ while (!reader.EndOfStream)
+ {
+ var text = reader.ReadLine();
+
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ lines.Add(text);
+ }
+ }
+
+ return lines;
+ }
+ }
+ }
+
}
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 6a9b7136c..071687c0f 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -19,6 +19,8 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Providers.Subtitles
{
@@ -28,33 +30,56 @@ namespace MediaBrowser.Providers.Subtitles
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _monitor;
- private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _config;
public event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
- public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IServerConfigurationManager config)
+ private ILocalizationManager _localization;
+
+ public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, IServerConfigurationManager config, ILocalizationManager localizationManager)
{
_logger = logger;
_fileSystem = fileSystem;
_monitor = monitor;
- _libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
_config = config;
+ _localization = localizationManager;
}
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
{
- _subtitleProviders = subtitleProviders.ToArray();
+ _subtitleProviders = subtitleProviders
+ .OrderBy(i =>
+ {
+ var hasOrder = i as IHasOrder;
+ return hasOrder == null ? 0 : hasOrder.Order;
+ })
+ .ToArray();
}
public async Task<RemoteSubtitleInfo[]> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
{
+ if (request.Language != null)
+ {
+ var culture = _localization.FindLanguageInfo(request.Language);
+
+ if (culture != null)
+ {
+ request.TwoLetterISOLanguageName = culture.TwoLetterISOLanguageName;
+ }
+ }
+
var contentType = request.ContentType;
var providers = _subtitleProviders
.Where(i => i.SupportedMediaTypes.Contains(contentType))
+ .Where(i => !request.DisabledSubtitleFetchers.Contains(i.Name, StringComparer.OrdinalIgnoreCase))
+ .OrderBy(i =>
+ {
+ var index = request.SubtitleFetcherOrder.ToList().IndexOf(i.Name);
+ return index == -1 ? int.MaxValue : index;
+ })
.ToArray();
// If not searching all, search one at a time until something is found
@@ -109,14 +134,22 @@ namespace MediaBrowser.Providers.Subtitles
return _config.GetConfiguration<SubtitleOptions>("subtitles");
}
+ public Task DownloadSubtitles(Video video, string subtitleId, CancellationToken cancellationToken)
+ {
+ var libraryOptions = BaseItem.LibraryManager.GetLibraryOptions(video);
+
+ return DownloadSubtitles(video, libraryOptions, subtitleId, cancellationToken);
+ }
+
public async Task DownloadSubtitles(Video video,
+ LibraryOptions libraryOptions,
string subtitleId,
CancellationToken cancellationToken)
{
var parts = subtitleId.Split(new[] { '_' }, 2);
var provider = GetProvider(parts.First());
- var saveInMediaFolder = video.IsSaveLocalMetadataEnabled();
+ var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
try
{
@@ -180,6 +213,8 @@ namespace MediaBrowser.Providers.Subtitles
try
{
+ _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(savePath));
+
using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
@@ -210,8 +245,7 @@ namespace MediaBrowser.Providers.Subtitles
public Task<RemoteSubtitleInfo[]> SearchSubtitles(Video video, string language, bool? isPerfectMatch, CancellationToken cancellationToken)
{
- if (video.LocationType != LocationType.FileSystem ||
- video.VideoType != VideoType.VideoFile)
+ if (video.VideoType != VideoType.VideoFile)
{
return Task.FromResult<RemoteSubtitleInfo[]>(new RemoteSubtitleInfo[] { });
}
@@ -275,12 +309,12 @@ namespace MediaBrowser.Providers.Subtitles
return _subtitleProviders.First(i => string.Equals(id, GetProviderId(i.Name)));
}
- public Task DeleteSubtitles(string itemId, int index)
+ public Task DeleteSubtitles(BaseItem item, int index)
{
var stream = _mediaSourceManager.GetMediaStreams(new MediaStreamQuery
{
Index = index,
- ItemId = new Guid(itemId),
+ ItemId = item.Id,
Type = MediaStreamType.Subtitle
}).First();
@@ -297,7 +331,7 @@ namespace MediaBrowser.Providers.Subtitles
_monitor.ReportFileSystemChangeComplete(path, false);
}
- return _libraryManager.GetItemById(itemId).RefreshMetadata(CancellationToken.None);
+ return item.RefreshMetadata(CancellationToken.None);
}
public Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken)
@@ -310,9 +344,8 @@ namespace MediaBrowser.Providers.Subtitles
return provider.GetSubtitles(id, cancellationToken);
}
- public SubtitleProviderInfo[] GetProviders(string itemId)
+ public SubtitleProviderInfo[] GetSupportedProviders(BaseItem video)
{
- var video = _libraryManager.GetItemById(itemId) as Video;
VideoContentType mediaType;
if (video is Episode)
diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
index 771570cbc..4f528086e 100644
--- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
@@ -8,8 +8,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
+using System.Collections.Generic;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Globalization;
@@ -34,9 +33,9 @@ namespace MediaBrowser.Providers.TV
_fileSystem = fileSystem;
}
- public async Task Run(Series series, CancellationToken cancellationToken)
+ public async Task<bool> Run(Series series, CancellationToken cancellationToken)
{
- await RemoveObsoleteSeasons(series).ConfigureAwait(false);
+ var seasonsRemoved = RemoveObsoleteSeasons(series);
var hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false);
@@ -49,6 +48,8 @@ namespace MediaBrowser.Providers.TV
//await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService))
// .ConfigureAwait(false);
}
+
+ return seasonsRemoved || hasNewSeasons;
}
private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
@@ -60,32 +61,44 @@ namespace MediaBrowser.Providers.TV
var hasChanges = false;
+ List<Season> seasons = null;
+
// Loop through the unique season numbers
foreach (var seasonNumber in episodesInSeriesFolder.Select(i => i.ParentIndexNumber ?? -1)
.Where(i => i >= 0)
.Distinct()
.ToList())
{
- var existingSeason = series.Children.OfType<Season>()
+ if (seasons == null)
+ {
+ seasons = series.Children.OfType<Season>().ToList();
+ }
+ var existingSeason = seasons
.FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber);
if (existingSeason == null)
{
await AddSeason(series, seasonNumber, false, cancellationToken).ConfigureAwait(false);
-
hasChanges = true;
+ seasons = null;
}
else if (existingSeason.IsVirtualItem)
{
existingSeason.IsVirtualItem = false;
existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+ seasons = null;
}
}
// Unknown season - create a dummy season to put these under
if (episodesInSeriesFolder.Any(i => !i.ParentIndexNumber.HasValue))
{
- var existingSeason = series.Children.OfType<Season>()
+ if (seasons == null)
+ {
+ seasons = series.Children.OfType<Season>().ToList();
+ }
+
+ var existingSeason = seasons
.FirstOrDefault(i => !i.IndexNumber.HasValue);
if (existingSeason == null)
@@ -93,11 +106,13 @@ namespace MediaBrowser.Providers.TV
await AddSeason(series, null, false, cancellationToken).ConfigureAwait(false);
hasChanges = true;
+ seasons = null;
}
else if (existingSeason.IsVirtualItem)
{
existingSeason.IsVirtualItem = false;
existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+ seasons = null;
}
}
@@ -137,7 +152,7 @@ namespace MediaBrowser.Providers.TV
return season;
}
- private async Task<bool> RemoveObsoleteSeasons(Series series)
+ private bool RemoveObsoleteSeasons(Series series)
{
var existingSeasons = series.Children.OfType<Season>().ToList();
@@ -177,13 +192,13 @@ namespace MediaBrowser.Providers.TV
foreach (var seasonToRemove in seasonsToRemove)
{
- _logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
+ _logger.Info("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber);
- await seasonToRemove.Delete(new DeleteOptions
+ _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions
{
DeleteFileLocation = true
- }).ConfigureAwait(false);
+ }, false);
hasChanges = true;
}
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 49175f62f..cea907ca3 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -35,14 +35,14 @@ namespace MediaBrowser.Providers.TV
}
var seriesId = item.FindSeriesId();
- if (item.SeriesId != seriesId)
+ if (!item.SeriesId.Equals(seriesId))
{
item.SeriesId = seriesId;
updateType |= ItemUpdateType.MetadataImport;
}
var seasonId = item.FindSeasonId();
- if (item.SeasonId != seasonId)
+ if (!item.SeasonId.Equals(seasonId))
{
item.SeasonId = seasonId;
updateType |= ItemUpdateType.MetadataImport;
@@ -80,21 +80,6 @@ namespace MediaBrowser.Providers.TV
targetItem.AirsBeforeEpisodeNumber = sourceItem.AirsBeforeEpisodeNumber;
}
- if (replaceData || !targetItem.DvdSeasonNumber.HasValue)
- {
- targetItem.DvdSeasonNumber = sourceItem.DvdSeasonNumber;
- }
-
- if (replaceData || !targetItem.DvdEpisodeNumber.HasValue)
- {
- targetItem.DvdEpisodeNumber = sourceItem.DvdEpisodeNumber;
- }
-
- if (replaceData || !targetItem.AbsoluteEpisodeNumber.HasValue)
- {
- targetItem.AbsoluteEpisodeNumber = sourceItem.AbsoluteEpisodeNumber;
- }
-
if (replaceData || !targetItem.IndexNumberEnd.HasValue)
{
targetItem.IndexNumberEnd = sourceItem.IndexNumberEnd;
diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
index 002c98e95..a89dee1e9 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
@@ -21,6 +21,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.TV
{
@@ -50,12 +51,12 @@ namespace MediaBrowser.Providers.TV
get { return "FanArt"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Season;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -153,7 +154,6 @@ namespace MediaBrowser.Providers.TV
PopulateImages(list, obj.showbackground, ImageType.Backdrop, 1920, 1080, seasonNumber);
}
- private Regex _regex_http = new Regex("^http://");
private void PopulateImages(List<RemoteImageInfo> list,
List<FanartSeriesProvider.Image> images,
ImageType type,
@@ -175,7 +175,7 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrEmpty(url) &&
!string.IsNullOrEmpty(season) &&
- int.TryParse(season, NumberStyles.Any, _usCulture, out imageSeasonNumber) &&
+ int.TryParse(season, NumberStyles.Integer, _usCulture, out imageSeasonNumber) &&
seasonNumber == imageSeasonNumber)
{
var likesString = i.likes;
@@ -188,11 +188,11 @@ namespace MediaBrowser.Providers.TV
Width = width,
Height = height,
ProviderName = Name,
- Url = _regex_http.Replace(url, "https://", 1),
+ Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
Language = i.lang
};
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
+ if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out likes))
{
info.CommunityRating = likes;
}
diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
index 33bf1a7c2..3ca013730 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
@@ -23,6 +23,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.TV
{
@@ -59,12 +60,12 @@ namespace MediaBrowser.Providers.TV
get { return "FanArt"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -77,7 +78,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
@@ -165,7 +166,6 @@ namespace MediaBrowser.Providers.TV
PopulateImages(list, obj.tvposter, ImageType.Primary, 1000, 1426);
}
- private Regex _regex_http = new Regex("^http://");
private void PopulateImages(List<RemoteImageInfo> list,
List<Image> images,
ImageType type,
@@ -198,11 +198,11 @@ namespace MediaBrowser.Providers.TV
Width = width,
Height = height,
ProviderName = Name,
- Url = _regex_http.Replace(url, "https://", 1),
+ Url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase),
Language = i.lang
};
- if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
+ if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Integer, _usCulture, out likes))
{
info.CommunityRating = likes;
}
@@ -273,7 +273,7 @@ namespace MediaBrowser.Providers.TV
if (fileInfo.Exists)
{
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
return;
}
@@ -379,7 +379,7 @@ namespace MediaBrowser.Providers.TV
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
- return new List<ConfigurationStore>
+ return new ConfigurationStore[]
{
new ConfigurationStore
{
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index b68e6e4a9..6e469a041 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Providers.TV
{
@@ -30,8 +31,6 @@ namespace MediaBrowser.Providers.TV
private readonly IFileSystem _fileSystem;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private static readonly SemaphoreSlim ResourceLock = new SemaphoreSlim(1, 1);
- public static bool IsRunning = false;
private readonly IXmlReaderSettingsFactory _xmlSettings;
public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
@@ -44,42 +43,9 @@ namespace MediaBrowser.Providers.TV
_xmlSettings = xmlSettings;
}
- public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken)
+ public async Task<bool> Run(Series series, bool addNewItems, CancellationToken cancellationToken)
{
- await ResourceLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- IsRunning = true;
-
- foreach (var seriesGroup in series)
- {
- try
- {
- await Run(seriesGroup, addNewItems, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (IOException ex)
- {
- //_logger.Warn("Series files missing for series id {0}", seriesGroup.Key);
- _logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key);
- }
- }
-
- IsRunning = false;
- ResourceLock.Release();
- }
-
- private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken)
- {
- var seriesList = group.ToList();
- var tvdbId = seriesList
- .Select(i => i.GetProviderId(MetadataProviders.Tvdb))
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ var tvdbId = series.GetProviderId(MetadataProviders.Tvdb);
// Todo: Support series by imdb id
var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -90,13 +56,13 @@ namespace MediaBrowser.Providers.TV
// Doesn't have required provider id's
if (string.IsNullOrWhiteSpace(seriesDataPath))
{
- return;
+ return false;
}
// Check this in order to avoid logging an exception due to directory not existing
if (!_fileSystem.DirectoryExists(seriesDataPath))
{
- return;
+ return false;
}
var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath)
@@ -120,64 +86,61 @@ namespace MediaBrowser.Providers.TV
if (int.TryParse(parts[2], NumberStyles.Integer, _usCulture, out episodeNumber))
{
- return new Tuple<int, int>(seasonNumber, episodeNumber);
+ return new ValueTuple<int, int>(seasonNumber, episodeNumber);
}
}
}
- return new Tuple<int, int>(-1, -1);
+ return new ValueTuple<int, int>(-1, -1);
})
.Where(i => i.Item1 != -1 && i.Item2 != -1)
.ToList();
- var hasBadData = HasInvalidContent(seriesList);
+ var allRecursiveChildren = series.GetRecursiveChildren();
+
+ var hasBadData = HasInvalidContent(series, allRecursiveChildren);
// Be conservative here to avoid creating missing episodes for ones they already have
- var addMissingEpisodes = !hasBadData && seriesList.All(i => _libraryManager.GetLibraryOptions(i).ImportMissingEpisodes);
+ var addMissingEpisodes = !hasBadData && _libraryManager.GetLibraryOptions(series).ImportMissingEpisodes;
- var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(seriesList, episodeLookup)
- .ConfigureAwait(false);
+ var anySeasonsRemoved = RemoveObsoleteOrMissingSeasons(series, allRecursiveChildren, episodeLookup);
- var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(seriesList, episodeLookup, addMissingEpisodes)
- .ConfigureAwait(false);
+ if (anySeasonsRemoved)
+ {
+ // refresh this
+ allRecursiveChildren = series.GetRecursiveChildren();
+ }
- var hasNewEpisodes = false;
+ var anyEpisodesRemoved = RemoveObsoleteOrMissingEpisodes(series, allRecursiveChildren, episodeLookup, addMissingEpisodes);
- if (addNewItems && seriesList.All(i => i.IsInternetMetadataEnabled()))
+ if (anyEpisodesRemoved)
{
- var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase));
+ // refresh this
+ allRecursiveChildren = series.GetRecursiveChildren();
+ }
- if (seriesConfig == null || !seriesConfig.DisabledMetadataFetchers.Contains(TvdbSeriesProvider.Current.Name, StringComparer.OrdinalIgnoreCase))
- {
- hasNewEpisodes = await AddMissingEpisodes(seriesList, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken)
- .ConfigureAwait(false);
- }
+ var hasNewEpisodes = false;
+
+ if (addNewItems && series.IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(series) ,TvdbSeriesProvider.Current.Name))
+ {
+ hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken)
+ .ConfigureAwait(false);
}
if (hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved)
{
- foreach (var series in seriesList)
- {
- var directoryService = new DirectoryService(_logger, _fileSystem);
-
- await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false);
-
- await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService), true)
- .ConfigureAwait(false);
- }
+ return true;
}
+
+ return false;
}
/// <summary>
/// Returns true if a series has any seasons or episodes without season or episode numbers
/// If this data is missing no virtual items will be added in order to prevent possible duplicates
/// </summary>
- /// <param name="group"></param>
- /// <returns></returns>
- private bool HasInvalidContent(IEnumerable<Series> group)
+ private bool HasInvalidContent(Series series, IList<BaseItem> allItems)
{
- var allItems = group.ToList().SelectMany(i => i.GetRecursiveChildren()).ToList();
-
return allItems.OfType<Season>().Any(i => !i.IndexNumber.HasValue) ||
allItems.OfType<Episode>().Any(i =>
{
@@ -198,18 +161,17 @@ namespace MediaBrowser.Providers.TV
/// </summary>
/// <param name="series">The series.</param>
/// <returns>Task.</returns>
- private async Task<bool> AddMissingEpisodes(List<Series> series,
+ private async Task<bool> AddMissingEpisodes(Series series,
+ IList<BaseItem> allItems,
bool addMissingEpisodes,
string seriesDataPath,
- IEnumerable<Tuple<int, int>> episodeLookup,
+ IEnumerable<ValueTuple<int, int>> episodeLookup,
CancellationToken cancellationToken)
{
- var existingEpisodes = (from s in series
- from c in s.GetRecursiveChildren(i => i is Episode).Cast<Episode>()
- select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0), c))
+ var existingEpisodes = allItems.OfType<Episode>()
.ToList();
- var lookup = episodeLookup as IList<Tuple<int, int>> ?? episodeLookup.ToList();
+ var lookup = episodeLookup as IList<ValueTuple<int, int>> ?? episodeLookup.ToList();
var seasonCounts = (from e in lookup
group e by e.Item1 into g
@@ -248,8 +210,6 @@ namespace MediaBrowser.Providers.TV
var now = DateTime.UtcNow;
- var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
-
now = now.AddDays(0 - UnairedEpisodeThresholdDays);
if (airDate.Value < now)
@@ -257,8 +217,8 @@ namespace MediaBrowser.Providers.TV
if (addMissingEpisodes)
{
// tvdb has a lot of nearly blank episodes
- _logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
- await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
+ _logger.Info("Creating virtual missing episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2);
+ await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true;
}
@@ -266,8 +226,8 @@ namespace MediaBrowser.Providers.TV
else if (airDate.Value > now)
{
// tvdb has a lot of nearly blank episodes
- _logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
- await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
+ _logger.Info("Creating virtual unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2);
+ await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true;
}
@@ -276,50 +236,37 @@ namespace MediaBrowser.Providers.TV
return hasChanges;
}
- private Series DetermineAppropriateSeries(List<Series> series, int seasonNumber)
- {
- if (series.Count == 1)
- {
- return series[0];
- }
-
- return series.FirstOrDefault(s => s.GetRecursiveChildren(i => i is Season).Any(season => (season.IndexNumber) == seasonNumber)) ??
- series.FirstOrDefault(s => s.GetRecursiveChildren(i => i is Season).Any(season => (season.IndexNumber) == 1)) ??
- series.OrderBy(s => s.GetRecursiveChildren(i => i is Season).Select(season => season.IndexNumber).Min()).First();
- }
-
/// <summary>
/// Removes the virtual entry after a corresponding physical version has been added
/// </summary>
- private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series,
- IEnumerable<Tuple<int, int>> episodeLookup,
+ private bool RemoveObsoleteOrMissingEpisodes(Series series,
+ IList<BaseItem> allRecursiveChildren,
+ IEnumerable<ValueTuple<int, int>> episodeLookup,
bool allowMissingEpisodes)
{
- var existingEpisodes = (from s in series
- from c in s.GetRecursiveChildren(i => i is Episode).Cast<Episode>()
- select new { Episode = c })
+ var existingEpisodes = allRecursiveChildren.OfType<Episode>()
.ToList();
var physicalEpisodes = existingEpisodes
- .Where(i => i.Episode.LocationType != LocationType.Virtual)
+ .Where(i => i.LocationType != LocationType.Virtual)
.ToList();
var virtualEpisodes = existingEpisodes
- .Where(i => i.Episode.LocationType == LocationType.Virtual)
+ .Where(i => i.LocationType == LocationType.Virtual)
.ToList();
var episodesToRemove = virtualEpisodes
.Where(i =>
{
- if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
+ if (i.IndexNumber.HasValue && i.ParentIndexNumber.HasValue)
{
- var seasonNumber = i.Episode.ParentIndexNumber.Value;
- var episodeNumber = i.Episode.IndexNumber.Value;
+ var seasonNumber = i.ParentIndexNumber.Value;
+ var episodeNumber = i.IndexNumber.Value;
// If there's a physical episode with the same season and episode number, delete it
if (physicalEpisodes.Any(p =>
- p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value) == seasonNumber &&
- p.Episode.ContainsEpisodeNumber(episodeNumber)))
+ p.ParentIndexNumber.HasValue && (p.ParentIndexNumber.Value) == seasonNumber &&
+ p.ContainsEpisodeNumber(episodeNumber)))
{
return true;
}
@@ -330,10 +277,10 @@ namespace MediaBrowser.Providers.TV
return true;
}
- if (!allowMissingEpisodes && i.Episode.IsMissingEpisode)
+ if (!allowMissingEpisodes && i.IsMissingEpisode)
{
// If it's missing, but not unaired, remove it
- if (!i.Episode.PremiereDate.HasValue || i.Episode.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < DateTime.Now.Date)
+ if (!i.PremiereDate.HasValue || i.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < DateTime.Now.Date)
{
return true;
}
@@ -348,13 +295,13 @@ namespace MediaBrowser.Providers.TV
var hasChanges = false;
- foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
+ foreach (var episodeToRemove in episodesToRemove)
{
- await episodeToRemove.Delete(new DeleteOptions
+ _libraryManager.DeleteItem(episodeToRemove, new DeleteOptions
{
DeleteFileLocation = true
- }).ConfigureAwait(false);
+ }, false);
hasChanges = true;
}
@@ -368,13 +315,11 @@ namespace MediaBrowser.Providers.TV
/// <param name="series">The series.</param>
/// <param name="episodeLookup">The episode lookup.</param>
/// <returns>Task{System.Boolean}.</returns>
- private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series,
- IEnumerable<Tuple<int, int>> episodeLookup)
+ private bool RemoveObsoleteOrMissingSeasons(Series series,
+ IList<BaseItem> allRecursiveChildren,
+ IEnumerable<ValueTuple<int, int>> episodeLookup)
{
- var existingSeasons = (from s in series
- from c in s.Children.OfType<Season>()
- select c)
- .ToList();
+ var existingSeasons = allRecursiveChildren.OfType<Season>().ToList();
var physicalSeasons = existingSeasons
.Where(i => i.LocationType != LocationType.Virtual)
@@ -384,6 +329,8 @@ namespace MediaBrowser.Providers.TV
.Where(i => i.LocationType == LocationType.Virtual)
.ToList();
+ var allEpisodes = allRecursiveChildren.OfType<Episode>().ToList();
+
var seasonsToRemove = virtualSeasons
.Where(i =>
{
@@ -397,10 +344,13 @@ namespace MediaBrowser.Providers.TV
return true;
}
- // If the season no longer exists in the remote lookup, delete it
+ // If the season no longer exists in the remote lookup, delete it, but only if an existing episode doesn't require it
if (episodeLookup.All(e => e.Item1 != seasonNumber))
{
- return true;
+ if (allEpisodes.All(s => s.ParentIndexNumber != seasonNumber || s.IsInSeasonFolder))
+ {
+ return true;
+ }
}
return false;
@@ -408,7 +358,7 @@ namespace MediaBrowser.Providers.TV
// Season does not have a number
// Remove if there are no episodes directly in series without a season number
- return i.Series.GetRecursiveChildren(e => e is Episode).Cast<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
+ return allEpisodes.All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
})
.ToList();
@@ -416,11 +366,11 @@ namespace MediaBrowser.Providers.TV
foreach (var seasonToRemove in seasonsToRemove)
{
- await seasonToRemove.Delete(new DeleteOptions
+ _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions
{
DeleteFileLocation = true
- }).ConfigureAwait(false);
+ }, false);
hasChanges = true;
}
@@ -456,7 +406,7 @@ namespace MediaBrowser.Providers.TV
ParentIndexNumber = seasonNumber,
Id = _libraryManager.GetNewItemId((series.Id + seasonNumber.ToString(_usCulture) + name), typeof(Episode)),
IsVirtualItem = true,
- SeasonId = season == null ? (Guid?)null : season.Id,
+ SeasonId = season == null ? Guid.Empty : season.Id,
SeriesId = series.Id
};
@@ -474,7 +424,7 @@ namespace MediaBrowser.Providers.TV
/// <param name="seasonCounts"></param>
/// <param name="tuple">The tuple.</param>
/// <returns>Episode.</returns>
- private Episode GetExistingEpisode(IList<Tuple<int, Episode>> existingEpisodes, Dictionary<int, int> seasonCounts, Tuple<int, int> tuple)
+ private Episode GetExistingEpisode(IList<Episode> existingEpisodes, Dictionary<int, int> seasonCounts, ValueTuple<int, int> tuple)
{
var s = tuple.Item1;
var e = tuple.Item2;
@@ -496,12 +446,10 @@ namespace MediaBrowser.Providers.TV
return null;
}
- private static Episode GetExistingEpisode(IEnumerable<Tuple<int, Episode>> existingEpisodes, int season, int episode)
+ private Episode GetExistingEpisode(IEnumerable<Episode> existingEpisodes, int season, int episode)
{
return existingEpisodes
- .Where(i => i.Item1 == season && i.Item2.ContainsEpisodeNumber(episode))
- .Select(i => i.Item2)
- .FirstOrDefault();
+ .FirstOrDefault(i => i.ParentIndexNumber == season && i.ContainsEpisodeNumber(episode));
}
/// <summary>
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
index 48af89830..e65c36d98 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
@@ -12,8 +12,7 @@ using MediaBrowser.Providers.Omdb;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
+using MediaBrowser.Common;
namespace MediaBrowser.Providers.TV
{
@@ -26,14 +25,16 @@ namespace MediaBrowser.Providers.TV
private readonly OmdbItemProvider _itemProvider;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
+ private readonly IApplicationHost _appHost;
- public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+ public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
- _itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager, fileSystem, configurationManager);
+ _appHost = appHost;
+ _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, logger, libraryManager, fileSystem, configurationManager);
}
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
@@ -60,7 +61,7 @@ namespace MediaBrowser.Providers.TV
{
if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
{
- result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager)
+ result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager)
.FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProviders.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index 5e3dc3c62..a9a026db9 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -30,13 +30,6 @@ namespace MediaBrowser.Providers.TV
}
}
- if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
- {
- var episodes = item.GetEpisodes();
- updateType |= SavePremiereDate(item, episodes);
- updateType |= SaveIsVirtualItem(item, episodes);
- }
-
var seriesName = item.FindSeriesName();
if (!string.Equals(item.SeriesName, seriesName, StringComparison.Ordinal))
{
@@ -52,7 +45,7 @@ namespace MediaBrowser.Providers.TV
}
var seriesId = item.FindSeriesId();
- if (item.SeriesId != seriesId)
+ if (!item.SeriesId.Equals(seriesId))
{
item.SeriesId = seriesId;
updateType |= ItemUpdateType.MetadataImport;
@@ -61,32 +54,37 @@ namespace MediaBrowser.Providers.TV
return updateType;
}
- protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override bool EnableUpdatingPremiereDateFromChildren
{
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ get
+ {
+ return true;
+ }
}
- private ItemUpdateType SavePremiereDate(Season item, List<BaseItem> episodes)
+ protected override IList<BaseItem> GetChildrenForMetadataUpdates(Season item)
{
- var dates = episodes.Where(i => i.PremiereDate.HasValue).Select(i => i.PremiereDate.Value).ToList();
+ return item.GetEpisodes();
+ }
- DateTime? premiereDate = null;
+ protected override ItemUpdateType UpdateMetadataFromChildren(Season item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ {
+ var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
- if (dates.Count > 0)
+ if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
- premiereDate = dates.Min();
+ updateType |= SaveIsVirtualItem(item, children);
}
- if (item.PremiereDate != premiereDate)
- {
- item.PremiereDate = premiereDate;
- return ItemUpdateType.MetadataEdit;
- }
+ return updateType;
+ }
- return ItemUpdateType.None;
+ protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
- private ItemUpdateType SaveIsVirtualItem(Season item, List<BaseItem> episodes)
+ private ItemUpdateType SaveIsVirtualItem(Season item, IList<BaseItem> episodes)
{
var isVirtualItem = item.LocationType == LocationType.Virtual && (episodes.Count == 0 || episodes.All(i => i.LocationType == LocationType.Virtual));
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index 3977ba9d9..338e7b4ed 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -9,38 +9,44 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-
-using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
public class SeriesMetadataService : MetadataService<Series, SeriesInfo>
{
private readonly ILocalizationManager _localization;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public SeriesMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager, ILocalizationManager localization) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
+ public SeriesMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager, ILocalizationManager localization, IXmlReaderSettingsFactory xmlSettings) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
_localization = localization;
+ _xmlSettings = xmlSettings;
}
protected override async Task AfterMetadataRefresh(Series item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false);
- if (refreshOptions.IsPostRecursiveRefresh)
- {
- var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager, FileSystem);
+ var seasonProvider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager, FileSystem);
+ await seasonProvider.Run(item, cancellationToken).ConfigureAwait(false);
+
+ var provider = new MissingEpisodeProvider(Logger,
+ ServerConfigurationManager,
+ LibraryManager,
+ _localization,
+ FileSystem,
+ _xmlSettings);
- try
- {
- await provider.Run(item, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in DummySeasonProvider", ex);
- }
+ try
+ {
+ await provider.Run(item, true, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error in DummySeasonProvider", ex);
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
index 728bbc505..f6568510d 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(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var episode = (Controller.Entities.TV.Episode)item;
var series = episode.Series;
@@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.TV
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
list.AddRange(GetPosters(response.images).Select(i => new RemoteImageInfo
{
@@ -124,7 +124,7 @@ namespace MediaBrowser.Providers.TV
get { return "TheMovieDb"; }
}
- public bool Supports(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Controller.Entities.TV.Episode;
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
index b19936480..4d7cec79f 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.TV
{
public abstract class MovieDbProviderBase
{
- private const string EpisodeUrlPattern = @"https://api.themoviedb.org/3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos";
+ private const string EpisodeUrlPattern = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos";
private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _configurationManager;
private readonly IJsonSerializer _jsonSerializer;
@@ -70,9 +70,9 @@ namespace MediaBrowser.Providers.TV
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
}
@@ -135,7 +135,7 @@ namespace MediaBrowser.Providers.TV
{
using (var json = response.Content)
{
- return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+ return await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
index 0c4c2d9ab..63484483f 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV
{
public class MovieDbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo>
{
- private const string GetTvInfo3 = @"https://api.themoviedb.org/3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos";
+ private const string GetTvInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos";
private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _configurationManager;
private readonly IJsonSerializer _jsonSerializer;
@@ -141,7 +141,6 @@ namespace MediaBrowser.Providers.TV
return _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath);
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureSeasonInfo(string tmdbId, int seasonNumber, string language, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(tmdbId))
@@ -160,9 +159,9 @@ namespace MediaBrowser.Providers.TV
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
@@ -224,7 +223,7 @@ namespace MediaBrowser.Providers.TV
{
using (var json = response.Content)
{
- return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+ return await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
index f05f7bb30..6124e7a9c 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -54,11 +54,11 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
- var results = await FetchImages((BaseItem)item, null, _jsonSerializer, cancellationToken).ConfigureAwait(false);
+ var results = await FetchImages(item, null, _jsonSerializer, cancellationToken).ConfigureAwait(false);
if (results == null)
{
@@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.TV
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var language = item.GetPreferredMetadataLanguage();
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
index 08099179c..6e9b9b239 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
@@ -27,7 +27,7 @@ namespace MediaBrowser.Providers.TV
{
public class MovieDbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
- private const string GetTvInfo3 = @"https://api.themoviedb.org/3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings";
+ private const string GetTvInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings";
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
internal static MovieDbSeriesProvider Current { get; private set; }
@@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.TV
var obj = _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath);
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var remoteResult = new RemoteSearchResult
{
@@ -208,13 +208,17 @@ namespace MediaBrowser.Providers.TV
result.Item = new Series();
result.ResultLanguage = seriesInfo.ResultLanguage;
- ProcessMainInfo(result.Item, seriesInfo, preferredCountryCode);
+ var settings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
+
+ ProcessMainInfo(result, seriesInfo, preferredCountryCode, settings);
return result;
}
- private void ProcessMainInfo(Series series, RootObject seriesInfo, string preferredCountryCode)
+ private void ProcessMainInfo(MetadataResult<Series> seriesResult, RootObject seriesInfo, string preferredCountryCode, TmdbSettingsResult settings)
{
+ var series = seriesResult.Item;
+
series.Name = seriesInfo.name;
series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.id.ToString(_usCulture));
@@ -237,10 +241,10 @@ namespace MediaBrowser.Providers.TV
if (seriesInfo.genres != null)
{
- series.Genres = seriesInfo.genres.Select(i => i.name).ToList();
+ series.Genres = seriesInfo.genres.Select(i => i.name).ToArray();
}
- series.HomePageUrl = seriesInfo.homepage;
+ //series.HomePageUrl = seriesInfo.homepage;
series.RunTimeTicks = seriesInfo.episode_run_time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault();
@@ -307,6 +311,35 @@ namespace MediaBrowser.Providers.TV
}
}
}
+
+ seriesResult.ResetPeople();
+ var tmdbImageUrl = settings.images.GetImageUrl("original");
+
+ if (seriesInfo.credits != null && seriesInfo.credits.cast != null)
+ {
+ foreach (var actor in seriesInfo.credits.cast.OrderBy(a => a.order))
+ {
+ var personInfo = new PersonInfo
+ {
+ Name = actor.name.Trim(),
+ Role = actor.character,
+ Type = PersonType.Actor,
+ SortOrder = actor.order
+ };
+
+ if (!string.IsNullOrWhiteSpace(actor.profile_path))
+ {
+ personInfo.ImageUrl = tmdbImageUrl + actor.profile_path;
+ }
+
+ if (actor.id > 0)
+ {
+ personInfo.SetProviderId(MetadataProviders.Tmdb, actor.id.ToString(CultureInfo.InvariantCulture));
+ }
+
+ seriesResult.AddPerson(personInfo);
+ }
+ }
}
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
@@ -362,7 +395,7 @@ namespace MediaBrowser.Providers.TV
{
using (var json = response.Content)
{
- mainResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
+ mainResult = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
if (!string.IsNullOrEmpty(language))
{
@@ -399,7 +432,7 @@ namespace MediaBrowser.Providers.TV
{
using (var json = response.Content)
{
- var englishResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
+ var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false);
mainResult.overview = englishResult.overview;
mainResult.ResultLanguage = "en";
@@ -410,7 +443,6 @@ namespace MediaBrowser.Providers.TV
return mainResult;
}
- private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureSeriesInfo(string tmdbId, string language, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(tmdbId))
@@ -425,9 +457,9 @@ namespace MediaBrowser.Providers.TV
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+ if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{
- return _cachedTask;
+ return Task.CompletedTask;
}
}
@@ -450,7 +482,7 @@ namespace MediaBrowser.Providers.TV
private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken)
{
- var url = string.Format("https://api.themoviedb.org/3/find/{0}?api_key={1}&external_source={2}",
+ var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/find/{0}?api_key={1}&external_source={2}",
id,
MovieDbProvider.ApiKey,
externalSource);
@@ -465,7 +497,7 @@ namespace MediaBrowser.Providers.TV
{
using (var json = response.Content)
{
- var result = _jsonSerializer.DeserializeFromStream<MovieDbSearch.ExternalIdLookupResult>(json);
+ var result = await _jsonSerializer.DeserializeFromStreamAsync<MovieDbSearch.ExternalIdLookupResult>(json).ConfigureAwait(false);
if (result != null && result.tv_results != null)
{
@@ -474,7 +506,7 @@ namespace MediaBrowser.Providers.TV
if (tv != null)
{
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
- var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+ var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
var remoteResult = new RemoteSearchResult
{
@@ -656,8 +688,8 @@ namespace MediaBrowser.Providers.TV
{
get
{
- // After Omdb and Tvdb
- return 2;
+ // After Tvdb
+ return 1;
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
index 7b6bcc4c7..23fefa484 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Episode;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var episode = (Episode)item;
var series = episode.Series;
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
index d823de786..41155aca1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
@@ -62,19 +62,9 @@ namespace MediaBrowser.Providers.TV
{
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
- var searchNumbers = new EpisodeNumbers();
-
- if (searchInfo.IndexNumber.HasValue)
- {
- searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
- }
-
- searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber;
- searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
-
try
{
- var metadataResult = FetchEpisodeData(searchInfo, searchNumbers, seriesDataPath, cancellationToken);
+ var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
if (metadataResult.HasMetadata)
{
@@ -119,21 +109,16 @@ namespace MediaBrowser.Providers.TV
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
(searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
{
- await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, null, null, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
-
- var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
+ var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, null, null, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
- var searchNumbers = new EpisodeNumbers();
- if (searchInfo.IndexNumber.HasValue)
+ if (string.IsNullOrEmpty(seriesDataPath))
{
- searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
+ return result;
}
- searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber;
- searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
try
{
- result = FetchEpisodeData(searchInfo, searchNumbers, seriesDataPath, cancellationToken);
+ result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
}
catch (FileNotFoundException)
{
@@ -176,13 +161,6 @@ namespace MediaBrowser.Providers.TV
}
}
- private class EpisodeNumbers
- {
- public int EpisodeNumber;
- public int? SeasonNumber;
- public int EpisodeNumberEnd;
- }
-
/// <summary>
/// Fetches the episode data.
/// </summary>
@@ -191,7 +169,7 @@ namespace MediaBrowser.Providers.TV
/// <param name="seriesDataPath">The series data path.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
- private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken)
+ private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken)
{
var result = new MetadataResult<Episode>()
{
@@ -207,7 +185,7 @@ namespace MediaBrowser.Providers.TV
if (xmlNodes.Count > 0)
{
- FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken);
+ FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken);
result.HasMetadata = true;
}
@@ -226,7 +204,7 @@ namespace MediaBrowser.Providers.TV
if (searchInfo.IndexNumber.HasValue)
{
- var files = GetEpisodeXmlFiles(searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, _fileSystem.GetDirectoryName(xmlFile));
+ var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, _fileSystem.GetDirectoryName(xmlFile));
list = files.Select(GetXmlReader).ToList();
}
@@ -239,7 +217,48 @@ namespace MediaBrowser.Providers.TV
return list;
}
- private List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
+ private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
+ {
+ if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase))
+ {
+ return string.Format("episode-abs-{0}.xml", episodeNumber);
+ }
+ else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+ {
+ return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
+ }
+ else
+ {
+ return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
+ }
+ }
+
+ private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
+ {
+ var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber));
+ var fileInfo = _fileSystem.GetFileInfo(file);
+
+ if (fileInfo.Exists)
+ {
+ return fileInfo;
+ }
+
+ if (!seasonNumber.HasValue)
+ {
+ return fileInfo;
+ }
+
+ // revert to aired order
+ if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+ {
+ file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber));
+ return _fileSystem.GetFileInfo(file);
+ }
+
+ return fileInfo;
+ }
+
+ private List<FileSystemMetadata> GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
{
var files = new List<FileSystemMetadata>();
@@ -248,27 +267,16 @@ namespace MediaBrowser.Providers.TV
return files;
}
- var usingAbsoluteData = false;
-
- if (seasonNumber.HasValue)
+ if (!seasonNumber.HasValue)
{
- var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
- var fileInfo = _fileSystem.GetFileInfo(file);
-
- if (fileInfo.Exists)
- {
- files.Add(fileInfo);
- }
+ seriesDisplayOrder = "absolute";
}
- else
+
+ var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
+
+ if (fileInfo.Exists)
{
- usingAbsoluteData = true;
- var file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber));
- var fileInfo = _fileSystem.GetFileInfo(file);
- if (fileInfo.Exists)
- {
- files.Add(fileInfo);
- }
+ files.Add(fileInfo);
}
var end = endingEpisodeNumber ?? episodeNumber;
@@ -276,18 +284,8 @@ namespace MediaBrowser.Providers.TV
while (episodeNumber <= end)
{
- string file;
+ fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
- if (usingAbsoluteData)
- {
- file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber));
- }
- else
- {
- file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
- }
-
- var fileInfo = _fileSystem.GetFileInfo(file);
if (fileInfo.Exists)
{
files.Add(fileInfo);
@@ -439,10 +437,15 @@ namespace MediaBrowser.Providers.TV
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken)
+ private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken)
{
var item = result.Item;
+ int? episodeNumber = null;
+ int? seasonNumber = null;
+ int? combinedEpisodeNumber = null;
+ int? combinedSeasonNumber = null;
+
// Use XmlReader for best performance
using (reader)
{
@@ -480,94 +483,70 @@ namespace MediaBrowser.Providers.TV
break;
}
- case "DVD_episodenumber":
+ case "EpisodeNumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
- float num;
+ int rval;
- if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ // int.TryParse is local aware, so it can be probamatic, force us culture
+ if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
{
- item.DvdEpisodeNumber = num;
+ episodeNumber = rval;
}
}
break;
}
- case "DVD_season":
+ case "SeasonNumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
- float num;
+ int rval;
- if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ // int.TryParse is local aware, so it can be probamatic, force us culture
+ if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
{
- item.DvdSeasonNumber = Convert.ToInt32(num);
+ seasonNumber = rval;
}
}
break;
}
- case "EpisodeNumber":
+ case "Combined_episodenumber":
{
var val = reader.ReadElementContentAsString();
- if (!item.IndexNumber.HasValue)
+ if (!string.IsNullOrWhiteSpace(val))
{
- if (!string.IsNullOrWhiteSpace(val))
- {
- int rval;
-
- // int.TryParse is local aware, so it can be probamatic, force us culture
- if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
- {
- item.IndexNumber = rval;
- }
- }
- }
-
- break;
- }
-
- case "SeasonNumber":
- {
- var val = reader.ReadElementContentAsString();
+ float num;
- if (!item.ParentIndexNumber.HasValue)
- {
- if (!string.IsNullOrWhiteSpace(val))
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
{
- int rval;
-
- // int.TryParse is local aware, so it can be probamatic, force us culture
- if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
- {
- item.ParentIndexNumber = rval;
- }
+ combinedEpisodeNumber = Convert.ToInt32(num);
}
}
break;
}
- case "absolute_number":
+ case "Combined_season":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
- int rval;
+ float num;
- // int.TryParse is local aware, so it can be probamatic, force us culture
- if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
{
- item.AbsoluteEpisodeNumber = rval;
+ combinedSeasonNumber = Convert.ToInt32(num);
}
}
@@ -771,6 +750,22 @@ namespace MediaBrowser.Providers.TV
}
}
}
+
+ if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+ {
+ episodeNumber = combinedEpisodeNumber ?? episodeNumber;
+ seasonNumber = combinedSeasonNumber ?? seasonNumber;
+ }
+
+ if (episodeNumber.HasValue)
+ {
+ item.IndexNumber = episodeNumber;
+ }
+
+ if (seasonNumber.HasValue)
+ {
+ item.ParentIndexNumber = seasonNumber;
+ }
}
private void AddPeople<T>(MetadataResult<T> result, string val, string personType)
@@ -787,30 +782,32 @@ namespace MediaBrowser.Providers.TV
private void AddGuestStars<T>(MetadataResult<T> result, string val)
where T : BaseItem
{
- // Sometimes tvdb actors have leading spaces
- //Regex Info:
- //The first block are the posible delimitators (open-parentheses should be there cause if dont the next block will fail)
- //The second block Allow the delimitators to be part of the text if they're inside parentheses
- var persons = Regex.Matches(val, @"(?<delimitators>([^|,(])|(?<ignoreinParentheses>\([^)]*\)*))+")
- .Cast<Match>()
- .Select(m => m.Value)
- .Where(i => !string.IsNullOrWhiteSpace(i) && !string.IsNullOrEmpty(i));
-
- foreach (var person in persons.Select(str =>
- {
- var nameGroup = str.Split(new[] { '(' }, 2, StringSplitOptions.RemoveEmptyEntries);
- var name = nameGroup[0].Trim();
- var roles = nameGroup.Count() > 1 ? nameGroup[1].Trim() : null;
- if (roles != null)
- roles = roles.EndsWith(")") ? roles.Substring(0, roles.Length - 1) : roles;
-
- return new PersonInfo { Type = PersonType.GuestStar, Name = name, Role = roles };
- }))
- {
- if (!string.IsNullOrWhiteSpace(person.Name))
+ // example:
+ // <GuestStars>|Mark C. Thomas| Dennis Kiefer| David Nelson (David)| Angela Nicholas| Tzi Ma| Kevin P. Kearns (Pasco)|</GuestStars>
+ var persons = val.Split('|')
+ .Select(i => i.Trim())
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .ToList();
+
+ foreach (var person in persons)
+ {
+ var index = person.IndexOf('(');
+ string role = null;
+ var name = person;
+
+ if (index != -1)
{
- result.AddPerson(person);
+ role = person.Substring(index + 1).Trim().TrimEnd(')');
+
+ name = person.Substring(0, index).Trim();
}
+
+ result.AddPerson(new PersonInfo
+ {
+ Type = PersonType.GuestStar,
+ Name = name,
+ Role = role
+ });
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
index ebcd61a6e..2839cb434 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
@@ -27,15 +27,17 @@ namespace MediaBrowser.Providers.TV
/// </summary>
public class TvdbPrescanTask : ILibraryPostScanTask
{
+ public const string TvdbBaseUrl = "https://thetvdb.com/";
+
/// <summary>
/// The server time URL
/// </summary>
- private const string ServerTimeUrl = "https://thetvdb.com/api/Updates.php?type=none";
+ private const string ServerTimeUrl = TvdbBaseUrl + "api/Updates.php?type=none";
/// <summary>
/// The updates URL
/// </summary>
- private const string UpdatesUrl = "https://thetvdb.com/api/Updates.php?type=all&time={0}";
+ private const string UpdatesUrl = TvdbBaseUrl + "api/Updates.php?type=all&time={0}";
/// <summary>
/// The _HTTP client
@@ -79,14 +81,6 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase));
-
- if (seriesConfig != null && seriesConfig.DisabledMetadataFetchers.Contains(TvdbSeriesProvider.Current.Name, StringComparer.OrdinalIgnoreCase))
- {
- progress.Report(100);
- return;
- }
-
var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
_fileSystem.CreateDirectory(path);
@@ -131,7 +125,7 @@ namespace MediaBrowser.Providers.TV
var missingSeries = seriesIdsInLibrary.Except(existingDirectories, StringComparer.OrdinalIgnoreCase)
.ToList();
- var enableInternetProviders = seriesList.Count == 0 ? false : seriesList[0].IsInternetMetadataEnabled();
+ var enableInternetProviders = seriesList.Count == 0 ? false : seriesList[0].IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(seriesList[0]), TvdbSeriesProvider.Current.Name);
if (!enableInternetProviders)
{
progress.Report(100);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
index 22dab1cd7..319ece156 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Season;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -63,7 +63,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var season = (Season)item;
var series = season.Series;
@@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.TV
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.Name, series.ProductionYear, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
- if (!string.IsNullOrWhiteSpace(seriesDataPath))
+ if (!string.IsNullOrEmpty(seriesDataPath))
{
var path = Path.Combine(seriesDataPath, "banners.xml");
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
index 6f3d763e2..e76ac6cd1 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(IHasMetadata item)
+ public bool Supports(BaseItem item)
{
return item is Series;
}
- public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
{
@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
if (TvdbSeriesProvider.IsValidSeries(item.ProviderIds))
{
@@ -72,6 +72,11 @@ namespace MediaBrowser.Providers.TV
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(item.ProviderIds, item.Name, item.ProductionYear, language, cancellationToken).ConfigureAwait(false);
+ if (string.IsNullOrEmpty(seriesDataPath))
+ {
+ return new RemoteImageInfo[] { };
+ }
+
var path = Path.Combine(seriesDataPath, "banners.xml");
try
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 846ab9558..8b7efc28b 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -36,10 +36,9 @@ namespace MediaBrowser.Providers.TV
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly ILocalizationManager _localizationManager;
- public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamFactory memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
+ public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
{
_zipClient = zipClient;
_httpClient = httpClient;
@@ -47,15 +46,17 @@ namespace MediaBrowser.Providers.TV
_config = config;
_logger = logger;
_libraryManager = libraryManager;
- _memoryStreamProvider = memoryStreamProvider;
_xmlSettings = xmlSettings;
_localizationManager = localizationManager;
Current = this;
}
- private const string SeriesSearchUrl = "https://www.thetvdb.com/api/GetSeries.php?seriesname={0}&language={1}";
- private const string SeriesGetZip = "https://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip";
- private const string GetSeriesByImdbId = "https://www.thetvdb.com/api/GetSeriesByRemoteID.php?imdbid={0}&language={1}";
+ public const string TvdbBaseUrl = "https://www.thetvdb.com/";
+
+ private const string SeriesSearchUrl = TvdbBaseUrl + "api/GetSeries.php?seriesname={0}&language={1}";
+ private const string SeriesGetZip = TvdbBaseUrl + "api/{0}/series/{1}/all/{2}.zip";
+ private const string GetSeriesByImdbId = TvdbBaseUrl + "api/GetSeriesByRemoteID.php?imdbid={0}&language={1}";
+ private const string GetSeriesByZap2ItId = TvdbBaseUrl + "api/GetSeriesByRemoteID.php?zap2it={0}&language={1}";
private string NormalizeLanguage(string language)
{
@@ -108,7 +109,12 @@ namespace MediaBrowser.Providers.TV
if (IsValidSeries(itemId.ProviderIds))
{
- await EnsureSeriesInfo(itemId.ProviderIds, itemId.Name, itemId.Year, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+ var seriesDataPath = await EnsureSeriesInfo(itemId.ProviderIds, itemId.Name, itemId.Year, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+
+ if (string.IsNullOrEmpty(seriesDataPath))
+ {
+ return result;
+ }
result.Item = new Series();
result.HasMetadata = true;
@@ -142,6 +148,11 @@ namespace MediaBrowser.Providers.TV
series.SetProviderId(MetadataProviders.Imdb, id);
}
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id) && !string.IsNullOrEmpty(id))
+ {
+ series.SetProviderId(MetadataProviders.Zap2It, id);
+ }
+
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
var seriesXmlPath = GetSeriesXmlPath(seriesProviderIds, metadataLanguage);
@@ -230,7 +241,7 @@ namespace MediaBrowser.Providers.TV
DeleteXmlFiles(seriesDataPath);
// Copy to memory stream because we need a seekable stream
- using (var ms = _memoryStreamProvider.CreateNew())
+ using (var ms = new MemoryStream())
{
await zipStream.CopyToAsync(ms).ConfigureAwait(false);
@@ -261,7 +272,15 @@ namespace MediaBrowser.Providers.TV
private async Task<string> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken)
{
- var url = string.Format(GetSeriesByImdbId, id, NormalizeLanguage(language));
+ String url;
+ if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
+ {
+ url = string.Format(GetSeriesByZap2ItId, id, NormalizeLanguage(language));
+ }
+ else
+ {
+ url = string.Format(GetSeriesByImdbId, id, NormalizeLanguage(language));
+ }
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
{
@@ -388,6 +407,15 @@ namespace MediaBrowser.Providers.TV
return true;
}
}
+
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
+ {
+ // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ return true;
+ }
+ }
return false;
}
@@ -421,7 +449,37 @@ namespace MediaBrowser.Providers.TV
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
{
- await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentNullException)
+ {
+ // Unable to determine tvdb id based on imdb id
+ return null;
+ }
+ }
+
+ return seriesDataPath;
+ }
+
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId))
+ {
+ var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
+
+ // Only download if not already there
+ // The post-scan task will take care of updates so we don't need to re-download here
+ if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
+ {
+ try
+ {
+ await DownloadSeriesZip(seriesId, MetadataProviders.Zap2It.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentNullException)
+ {
+ // Unable to determine tvdb id based on Zap2It id
+ return null;
+ }
}
return seriesDataPath;
@@ -516,10 +574,11 @@ namespace MediaBrowser.Providers.TV
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>();
var comparableName = GetComparableName(name);
+ var list = new List<Tuple<List<string>, RemoteSearchResult>>();
+
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
{
Url = url,
@@ -562,11 +621,11 @@ namespace MediaBrowser.Providers.TV
}
using (var subtree = reader.ReadSubtree())
{
- var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName);
- if (searchResult != null)
+ var searchResultInfo = GetSeriesSearchResultFromSubTree(subtree);
+ if (searchResultInfo != null)
{
- searchResult.SearchProviderName = Name;
- searchResults.Add(searchResult);
+ searchResultInfo.Item2.SearchProviderName = Name;
+ list.Add(searchResultInfo);
}
}
break;
@@ -587,22 +646,21 @@ namespace MediaBrowser.Providers.TV
}
}
- if (searchResults.Count == 0)
- {
- _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org.");
- }
-
- return searchResults;
+ return list
+ .OrderBy(i => i.Item1.Contains(comparableName, StringComparer.OrdinalIgnoreCase) ? 0 : 1)
+ .ThenBy(i => list.IndexOf(i))
+ .Select(i => i.Item2)
+ .ToList();
}
- private RemoteSearchResult GetSeriesSearchResultFromSubTree(XmlReader reader, string comparableName)
+ private Tuple<List<string>, RemoteSearchResult> GetSeriesSearchResultFromSubTree(XmlReader reader)
{
var searchResult = new RemoteSearchResult
{
SearchProviderName = Name
};
- var titles = new List<string>();
+ var tvdbTitles = new List<string>();
string seriesId = null;
reader.MoveToContent();
@@ -621,7 +679,7 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrWhiteSpace(val))
{
- titles.Add(GetComparableName(val));
+ tvdbTitles.Add(GetComparableName(val));
}
break;
}
@@ -631,7 +689,7 @@ namespace MediaBrowser.Providers.TV
var val = reader.ReadElementContentAsString();
var alias = (val ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(GetComparableName);
- titles.AddRange(alias);
+ tvdbTitles.AddRange(alias);
break;
}
@@ -695,22 +753,15 @@ namespace MediaBrowser.Providers.TV
}
}
- foreach (var title in titles)
+ if (tvdbTitles.Count == 0)
{
- if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase))
- {
- if (!string.IsNullOrWhiteSpace(seriesId))
- {
- searchResult.Name = title;
- searchResult.SetProviderId(MetadataProviders.Tvdb, seriesId);
- return searchResult;
- }
- break;
- }
- _logger.Info("TVDb Provider - " + title + " did not match " + comparableName);
+ return null;
}
- return null;
+ searchResult.Name = tvdbTitles.FirstOrDefault();
+ searchResult.SetProviderId(MetadataProviders.Tvdb, seriesId);
+
+ return new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, searchResult);
}
/// <summary>
@@ -1081,6 +1132,12 @@ namespace MediaBrowser.Providers.TV
{
switch (reader.Name)
{
+ case "id":
+ {
+ item.SetProviderId(MetadataProviders.Tvdb.ToString(), (reader.ReadElementContentAsString() ?? string.Empty).Trim());
+ break;
+ }
+
case "SeriesName":
{
item.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
@@ -1100,26 +1157,26 @@ namespace MediaBrowser.Providers.TV
}
case "Airs_DayOfWeek":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
{
- item.AirDays = TVUtils.GetAirDays(val);
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirDays = TVUtils.GetAirDays(val);
+ }
+ break;
}
- break;
- }
case "Airs_Time":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
{
- item.AirTime = val;
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AirTime = val;
+ }
+ break;
}
- break;
- }
case "ContentRating":
{
@@ -1256,7 +1313,7 @@ namespace MediaBrowser.Providers.TV
if (vals.Count > 0)
{
- item.Genres.Clear();
+ item.Genres = Array.Empty<string>();
foreach (var genre in vals)
{
@@ -1369,6 +1426,9 @@ namespace MediaBrowser.Providers.TV
var absoluteNumber = -1;
var lastUpdateString = string.Empty;
+ var dvdSeasonNumber = -1;
+ var dvdEpisodeNumber = -1.0;
+
using (var streamReader = new StringReader(xml))
{
// Use XmlReader for best performance
@@ -1404,6 +1464,40 @@ namespace MediaBrowser.Providers.TV
break;
}
+ case "Combined_episodenumber":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ float num;
+
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ {
+ dvdEpisodeNumber = num;
+ }
+ }
+
+ break;
+ }
+
+ case "Combined_season":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ float num;
+
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ {
+ dvdSeasonNumber = Convert.ToInt32(num);
+ }
+ }
+
+ break;
+ }
+
case "absolute_number":
{
var val = reader.ReadElementContentAsString();
@@ -1493,6 +1587,27 @@ namespace MediaBrowser.Providers.TV
}
}
}
+
+ if (dvdSeasonNumber != -1 && dvdEpisodeNumber != -1 && (dvdSeasonNumber != seasonNumber || dvdEpisodeNumber != episodeNumber))
+ {
+ file = Path.Combine(seriesDataPath, string.Format("episode-dvd-{0}-{1}.xml", dvdSeasonNumber, dvdEpisodeNumber));
+
+ // Only save the file if not already there, or if the episode has changed
+ if (hasEpisodeChanged || !_fileSystem.FileExists(file))
+ {
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
+ {
+ using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ Async = true
+ }))
+ {
+ await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ }
+ }
+ }
+ }
}
/// <summary>
@@ -1518,6 +1633,13 @@ namespace MediaBrowser.Providers.TV
return seriesDataPath;
}
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId))
+ {
+ var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
+
+ return seriesDataPath;
+ }
+
return null;
}
diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs
index 104bcb5ae..6d5f4abbb 100644
--- a/MediaBrowser.Providers/TV/TvExternalIds.cs
+++ b/MediaBrowser.Providers/TV/TvExternalIds.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.TV
public string UrlFormatString
{
- get { return "http://tvlistings.zap2it.com/tv/dexter/{0}?aid=zap2it"; }
+ get { return "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV
public string UrlFormatString
{
- get { return "https://thetvdb.com/index.php?tab=series&id={0}"; }
+ get { return TvdbPrescanTask.TvdbBaseUrl + "?tab=series&id={0}"; }
}
public bool Supports(IHasProviderIds item)
@@ -88,7 +88,7 @@ namespace MediaBrowser.Providers.TV
public string UrlFormatString
{
- get { return "https://thetvdb.com/index.php?tab=episode&id={0}"; }
+ get { return TvdbPrescanTask.TvdbBaseUrl + "?tab=episode&id={0}"; }
}
public bool Supports(IHasProviderIds item)
diff --git a/MediaBrowser.Server.Mono/EmbyServer.csproj b/MediaBrowser.Server.Mono/EmbyServer.csproj
new file mode 100644
index 000000000..03cb823c1
--- /dev/null
+++ b/MediaBrowser.Server.Mono/EmbyServer.csproj
@@ -0,0 +1,69 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <DebugType>None</DebugType>
+ <RuntimeIdentifiers>ubuntu.16.04-x64</RuntimeIdentifiers>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.2.0" />
+ <PackageReference Include="sharpcompress" Version="0.22.0" />
+ <PackageReference Include="SimpleInjector" Version="4.3.0" />
+ <PackageReference Include="SkiaSharp" Version="1.60.3" />
+ <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.11" />
+ <PackageReference Include="SQLitePCLRaw.core" Version="1.1.11" />
+ <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.11" />
+ <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0" />
+ <Reference Include="Emby.Server.Connect">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Sync">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.MediaEncoding">
+ <HintPath>..\ThirdParty\emby\Emby.Server.MediaEncoding.dll</HintPath>
+ </Reference>
+ <Reference Include="MediaBrowser.IsoMounting.Linux">
+ <HintPath>..\ThirdParty\MediaBrowser.IsoMounting.Linux\MediaBrowser.IsoMounting.Linux.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\BDInfo\BDInfo.csproj" />
+ <ProjectReference Include="..\DvdLib\DvdLib.csproj" />
+ <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
+ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
+ <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
+ <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
+ <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
+ <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
+ <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
+ <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
+ <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj" />
+ <ProjectReference Include="..\RSSDP\RSSDP.csproj" />
+ <ProjectReference Include="..\Emby.Drawing.Skia\Emby.Drawing.Skia.csproj" />
+ <ProjectReference Include="..\Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj" />
+ <ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="..\SharedVersion.cs" />
+ </ItemGroup>
+
+</Project>
diff --git a/MediaBrowser.Server.Mono/ImageMagickSharp.dll.config b/MediaBrowser.Server.Mono/ImageMagickSharp.dll.config
deleted file mode 100644
index a83ca85c9..000000000
--- a/MediaBrowser.Server.Mono/ImageMagickSharp.dll.config
+++ /dev/null
@@ -1,5 +0,0 @@
-<configuration>
- <dllmap dll="CORE_RL_Wand_.dll" target="libMagickWand-6.Q8.so" os="linux"/>
- <dllmap dll="CORE_RL_Wand_.dll" target="libMagickWand-6.so" os="freebsd,openbsd,netbsd"/>
- <dllmap dll="CORE_RL_Wand_.dll" target="./MediaInfo/osx/libmediainfo.dylib" os="osx"/>
-</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
deleted file mode 100644
index dfd071447..000000000
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ /dev/null
@@ -1,197 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>10.0.0</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{175A9388-F352-4586-A6B4-070DED62B644}</ProjectGuid>
- <OutputType>Exe</OutputType>
- <RootNamespace>MediaBrowser.Server.Mono</RootNamespace>
- <AssemblyName>MediaBrowser.Server.Mono</AssemblyName>
- <StartupObject>MediaBrowser.Server.Mono.MainClass</StartupObject>
- <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <TargetFrameworkProfile />
- <NuGetPackageImportStamp>
- </NuGetPackageImportStamp>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <PlatformTarget>AnyCPU</PlatformTarget>
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <PlatformTarget>AnyCPU</PlatformTarget>
- <DebugType>none</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="Emby.Server.CinemaMode">
- <HintPath>..\ThirdParty\emby\Emby.Server.CinemaMode.dll</HintPath>
- </Reference>
- <Reference Include="Emby.Server.Connect">
- <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
- </Reference>
- <Reference Include="Emby.Server.Sync">
- <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
- </Reference>
- <Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.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="SharpCompress, Version=0.18.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
- <HintPath>..\packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll</HintPath>
- </Reference>
- <Reference Include="SimpleInjector, Version=4.0.12.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.4.0.12\lib\net45\SimpleInjector.dll</HintPath>
- </Reference>
- <Reference Include="SkiaSharp, Version=1.58.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
- <HintPath>..\packages\SkiaSharp.1.58.1\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.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.8\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="MediaBrowser.IsoMounting.Linux">
- <HintPath>..\ThirdParty\MediaBrowser.IsoMounting.Linux\MediaBrowser.IsoMounting.Linux.dll</HintPath>
- </Reference>
- <Reference Include="System.Configuration" />
- <Reference Include="System.Data" />
- <Reference Include="System.IO.Compression" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="ApplicationPathHelper.cs" />
- <Compile Include="ImageEncoderHelper.cs" />
- <Compile Include="MonoAppHost.cs" />
- <Compile Include="Native\MonoFileSystem.cs" />
- <Compile Include="Native\PowerManagement.cs" />
- <Compile Include="Program.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <ItemGroup>
- <ProjectReference Include="..\BDInfo\BDInfo.csproj">
- <Project>{88ae38df-19d7-406f-a6a9-09527719a21e}</Project>
- <Name>BDInfo</Name>
- </ProjectReference>
- <ProjectReference Include="..\DvdLib\DvdLib.csproj">
- <Project>{713f42b5-878e-499d-a878-e4c652b1d5e8}</Project>
- <Name>DvdLib</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.ImageMagick\Emby.Drawing.ImageMagick.csproj">
- <Project>{6cfee013-6e7c-432b-ac37-cabf0880c69a}</Project>
- <Name>Emby.Drawing.ImageMagick</Name>
- </ProjectReference>
- <ProjectReference Include="..\Emby.Drawing.Skia\Emby.Drawing.Skia.csproj">
- <Project>{2312da6d-ff86-4597-9777-bceec32d96dd}</Project>
- <Name>Emby.Drawing.Skia</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.WebDashboard\MediaBrowser.WebDashboard.csproj">
- <Project>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</Project>
- <Name>MediaBrowser.WebDashboard</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj">
- <Project>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</Project>
- <Name>MediaBrowser.Providers</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
- <Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
- <Name>MediaBrowser.Controller</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj">
- <Project>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</Project>
- <Name>MediaBrowser.Api</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
- <Project>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</Project>
- <Name>MediaBrowser.LocalMetadata</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="..\RSSDP\RSSDP.csproj">
- <Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
- <Name>RSSDP</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <None Include="..\ThirdParty\SQLite3\osx\libsqlite3.0.dylib">
- <Link>libsqlite3.0.dylib</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="..\ThirdParty\SQLite3\osx\libsqlite3.dylib">
- <Link>libsqlite3.dylib</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="app.config" />
- <None Include="ImageMagickSharp.dll.config">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="packages.config" />
- <None Include="SkiaSharp.dll.config">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="SQLitePCLRaw.provider.sqlite3.dll.config">
- <SubType>Designer</SubType>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- </ItemGroup>
- <ItemGroup />
-</Project> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs
index 609497eed..dcf10b47f 100644
--- a/MediaBrowser.Server.Mono/MonoAppHost.cs
+++ b/MediaBrowser.Server.Mono/MonoAppHost.cs
@@ -1,15 +1,21 @@
using System;
using System.Collections.Generic;
using System.Reflection;
-using Emby.Server.CinemaMode;
+using System.Threading;
+using System.Threading.Tasks;
+//using Emby.Server.CinemaMode;
using Emby.Server.Connect;
using Emby.Server.Implementations;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.Net;
using Emby.Server.Sync;
using MediaBrowser.Controller.Connect;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync;
using MediaBrowser.IsoMounter;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
namespace MediaBrowser.Server.Mono
@@ -34,10 +40,10 @@ namespace MediaBrowser.Server.Mono
return new ConnectManager();
}
- protected override ISyncManager CreateSyncManager()
- {
- return new SyncManager();
- }
+ //protected override ISyncManager CreateSyncManager()
+ //{
+ // return new SyncManager();
+ //}
protected override void RestartInternal()
{
@@ -49,19 +55,8 @@ namespace MediaBrowser.Server.Mono
var list = new List<Assembly>();
list.Add(GetType().Assembly);
- list.AddRange(GetLinuxAssemblies());
-
- return list;
- }
-
- private IEnumerable<Assembly> GetLinuxAssemblies()
- {
- var list = new List<Assembly>();
-
- list.Add(typeof(DefaultIntroProvider).Assembly);
list.Add(typeof(ConnectManager).Assembly);
- list.Add(typeof(SyncManager).Assembly);
- list.Add(typeof(LinuxIsoManager).Assembly);
+ list.Add(typeof(Emby.Server.Sync.SyncManager).Assembly);
return list;
}
@@ -96,5 +91,20 @@ namespace MediaBrowser.Server.Mono
return new Version(1, 0);
}
+
+ protected override IHttpListener CreateHttpListener()
+ {
+ return new EmbyServer.SocketSharp.WebSocketSharpListener(LogManager.GetLogger("HttpServer"),
+ Certificate,
+ StreamHelper,
+ TextEncoding,
+ NetworkManager,
+ SocketFactory,
+ CryptographyProvider,
+ SupportsDualModeSockets,
+ FileSystemManager,
+ EnvironmentInfo);
+ }
+
}
}
diff --git a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
index e6b77991c..2fd95ab82 100644
--- a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
+++ b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
@@ -7,8 +7,8 @@ namespace MediaBrowser.Server.Mono.Native
{
public class MonoFileSystem : ManagedFileSystem
{
- public MonoFileSystem(ILogger logger, IEnvironmentInfo environment, string tempPath)
- : base(logger, environment, tempPath)
+ public MonoFileSystem(ILogger logger, IEnvironmentInfo environment, string defaultDirectory, string tempPath, bool enableSeperateFileAndDirectoryQueries)
+ : base(logger, environment,defaultDirectory, tempPath, enableSeperateFileAndDirectoryQueries)
{
}
diff --git a/MediaBrowser.Server.Mono/Native/PowerManagement.cs b/MediaBrowser.Server.Mono/Native/PowerManagement.cs
index 0fdd4de80..219a69d65 100644
--- a/MediaBrowser.Server.Mono/Native/PowerManagement.cs
+++ b/MediaBrowser.Server.Mono/Native/PowerManagement.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.System;
+using System;
+using MediaBrowser.Model.System;
namespace MediaBrowser.Server.Mono.Native
{
@@ -11,5 +12,10 @@ namespace MediaBrowser.Server.Mono.Native
public void AllowSystemStandby()
{
}
+
+ public void ScheduleWake(DateTime wakeTimeUtc, string displayName)
+ {
+ // nothing to Do
+ }
}
}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index 7a3a968ad..3bcfff983 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -23,6 +23,7 @@ using MediaBrowser.Model.System;
using Mono.Unix.Native;
using ILogger = MediaBrowser.Model.Logging.ILogger;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
+using System.Threading;
namespace MediaBrowser.Server.Mono
{
@@ -54,7 +55,8 @@ namespace MediaBrowser.Server.Mono
{
_logManager = logManager;
- logManager.ReloadLogger(LogSeverity.Info);
+ var task = logManager.ReloadLogger(LogSeverity.Debug, CancellationToken.None);
+ Task.WaitAll(task);
logManager.AddConsoleOutput();
var logger = _logger = logManager.GetLogger("Main");
@@ -76,7 +78,9 @@ namespace MediaBrowser.Server.Mono
private static void SetSqliteProvider()
{
- SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+ // SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+ //SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+ SQLitePCL.Batteries_V2.Init();
}
private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, string programDataPath)
@@ -98,7 +102,7 @@ namespace MediaBrowser.Server.Mono
var environmentInfo = GetEnvironmentInfo();
- var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, appPaths.TempDirectory);
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true);
FileSystem = fileSystem;
@@ -107,11 +111,11 @@ namespace MediaBrowser.Server.Mono
options,
fileSystem,
new PowerManagement(),
- "emby.mono.zip",
+ "embyserver-mono_{version}.zip",
environmentInfo,
new NullImageEncoder(),
new SystemEvents(logManager.GetLogger("SystemEvents")),
- new NetworkManager(logManager.GetLogger("NetworkManager"))))
+ new NetworkManager(logManager.GetLogger("NetworkManager"), environmentInfo)))
{
if (options.ContainsOption("-v"))
{
@@ -121,17 +125,13 @@ namespace MediaBrowser.Server.Mono
Console.WriteLine("appHost.Init");
- var initProgress = new Progress<double>();
-
- var task = appHost.Init(initProgress);
-
- Task.WaitAll(task);
+ appHost.Init();
appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager);
Console.WriteLine("Running startup tasks");
- task = appHost.RunStartupTasks();
+ var task = appHost.RunStartupTasks();
Task.WaitAll(task);
task = ApplicationTaskCompletionSource.Task;
@@ -293,19 +293,20 @@ namespace MediaBrowser.Server.Mono
}
}
- class NoCheckCertificatePolicy : ICertificatePolicy
- {
- public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
- {
- return true;
- }
- }
-
+ // class NoCheckCertificatePolicy : ICertificatePolicy
+ // {
+ // public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
+ // {
+ // return true;
+ // }
+ // }
+
public class MonoEnvironmentInfo : EnvironmentInfo
{
- public override string GetUserId()
- {
- return Syscall.getuid().ToString(CultureInfo.InvariantCulture);
- }
+
+ //public override string GetUserId()
+ //{
+ // return Syscall.getuid().ToString(CultureInfo.InvariantCulture);
+ //}
}
}
diff --git a/MediaBrowser.Server.Mono/Properties/launchSettings.json b/MediaBrowser.Server.Mono/Properties/launchSettings.json
new file mode 100644
index 000000000..c65988f99
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Properties/launchSettings.json
@@ -0,0 +1,7 @@
+{
+ "profiles": {
+ "EmbyServer": {
+ "commandName": "Project"
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config b/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config
deleted file mode 100644
index 793d8a889..000000000
--- a/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<configuration>
- <dllmap dll="sqlite3" target="libsqlite3.so" os="linux"/>
- <dllmap dll="dl" target="libdl.so" os="linux"/>
-</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/SkiaSharp.dll.config b/MediaBrowser.Server.Mono/SkiaSharp.dll.config
deleted file mode 100644
index fe0650f64..000000000
--- a/MediaBrowser.Server.Mono/SkiaSharp.dll.config
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<configuration>
- <dllmap dll="libSkiaSharp.dll" target="libSkiaSharp.dylib" os="osx" />
- <dllmap dll="libSkiaSharp.dll" target="libSkiaSharp.so" os="!windows,osx" />
-</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs b/MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs
new file mode 100644
index 000000000..1e7c93deb
--- /dev/null
+++ b/MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs
@@ -0,0 +1,14 @@
+using MediaBrowser.Model.Services;
+using System.IO;
+
+namespace EmbyServer.SocketSharp
+{
+ public class HttpFile : IHttpFile
+ {
+ public string Name { get; set; }
+ public string FileName { get; set; }
+ public long ContentLength { get; set; }
+ public string ContentType { get; set; }
+ public Stream InputStream { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs b/MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs
index ec14c32c8..9d2354316 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs
+++ b/MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs
@@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
+namespace EmbyServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
@@ -33,17 +33,17 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return header.Substring(ap + 1, end - ap - 1);
}
- async Task LoadMultiPart()
+ async Task LoadMultiPart(WebROCollection form)
{
string boundary = GetParameter(ContentType, "; boundary=");
if (boundary == null)
return;
- using (var requestStream = GetSubStream(InputStream, _memoryStreamProvider))
+ using (var requestStream = InputStream)
{
//DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request
//Not ending with \r\n?
- var ms = _memoryStreamProvider.CreateNew(32 * 1024);
+ var ms = new MemoryStream(32 * 1024);
await requestStream.CopyToAsync(ms).ConfigureAwait(false);
var input = ms;
@@ -83,28 +83,19 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
}
}
- public QueryParamCollection Form
+ public async Task<QueryParamCollection> GetFormData()
{
- get
- {
- if (form == null)
- {
- form = new WebROCollection();
- files = new Dictionary<string, HttpPostedFile>();
+ var form = new WebROCollection();
+ files = new Dictionary<string, HttpPostedFile>();
- if (IsContentType("multipart/form-data", true))
- {
- var task = LoadMultiPart();
- Task.WaitAll(task);
- }
- else if (IsContentType("application/x-www-form-urlencoded", true))
- {
- var task = LoadWwwForm();
- Task.WaitAll(task);
- }
-
- form.Protect();
- }
+ if (IsContentType("multipart/form-data", true))
+ {
+ await LoadMultiPart(form).ConfigureAwait(false);
+ }
+ else if (IsContentType("application/x-www-form-urlencoded", true))
+ {
+ await LoadWwwForm(form).ConfigureAwait(false);
+ }
#if NET_4_0
if (validateRequestNewMode && !checked_form) {
@@ -114,14 +105,13 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
ValidateNameValueCollection ("Form", query_string_nvc, RequestValidationSource.Form);
} else
#endif
- if (validate_form && !checked_form)
- {
- checked_form = true;
- ValidateNameValueCollection("Form", form);
- }
-
- return form;
+ if (validate_form && !checked_form)
+ {
+ checked_form = true;
+ ValidateNameValueCollection("Form", form);
}
+
+ return form;
}
public string Accept
@@ -228,11 +218,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
}
- async Task LoadWwwForm()
+ async Task LoadWwwForm(WebROCollection form)
{
- using (Stream input = GetSubStream(InputStream, _memoryStreamProvider))
+ using (Stream input = InputStream)
{
- using (var ms = _memoryStreamProvider.CreateNew())
+ using (var ms = new MemoryStream())
{
await input.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
@@ -252,7 +242,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
if (c == '&')
{
- AddRawKeyValue(key, value);
+ AddRawKeyValue(form, key, value);
break;
}
else
@@ -260,23 +250,23 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
}
if (c == -1)
{
- AddRawKeyValue(key, value);
+ AddRawKeyValue(form, key, value);
return;
}
}
else if (c == '&')
- AddRawKeyValue(key, value);
+ AddRawKeyValue(form, key, value);
else
key.Append((char)c);
}
if (c == -1)
- AddRawKeyValue(key, value);
+ AddRawKeyValue(form, key, value);
}
}
}
}
- void AddRawKeyValue(StringBuilder key, StringBuilder value)
+ void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value)
{
string decodedKey = WebUtility.UrlDecode(key.ToString());
form.Add(decodedKey,
@@ -286,39 +276,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
value.Length = 0;
}
- WebROCollection form;
-
Dictionary<string, HttpPostedFile> files;
class WebROCollection : QueryParamCollection
{
- bool got_id;
- int id;
-
- public bool GotID
- {
- get { return got_id; }
- }
-
- public int ID
- {
- get { return id; }
- set
- {
- got_id = true;
- id = value;
- }
- }
- public void Protect()
- {
- //IsReadOnly = true;
- }
-
- public void Unprotect()
- {
- //IsReadOnly = false;
- }
-
public override string ToString()
{
StringBuilder result = new StringBuilder();
@@ -533,7 +494,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public static bool StartsWith(string str1, string str2, bool ignore_case)
{
- if (string.IsNullOrWhiteSpace(str1))
+ if (string.IsNullOrEmpty(str1))
{
return false;
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs b/MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs
index cc7a4557e..6c2dd0f76 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs
+++ b/MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs
@@ -1,12 +1,12 @@
using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
-using WebSocketState = MediaBrowser.Model.Net.WebSocketState;
+using System.Net.WebSockets;
+using Emby.Server.Implementations.Net;
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
+namespace EmbyServer.SocketSharp
{
public class SharpWebSocket : IWebSocket
{
@@ -23,6 +23,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
/// <value>The web socket.</value>
private SocketHttpListener.WebSocket WebSocket { get; set; }
+ private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger)
@@ -47,6 +48,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
WebSocket.ConnectAsServer();
}
+ public Task StartReceive()
+ {
+ return _taskCompletionSource.Task;
+ }
+
void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e)
{
_logger.Error("Error in SharpWebSocket: {0}", e.Message ?? string.Empty);
@@ -55,12 +61,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e)
{
+ _taskCompletionSource.TrySetResult(true);
+
EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger);
}
void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e)
{
- //if (!string.IsNullOrWhiteSpace(e.Data))
+ //if (!string.IsNullOrEmpty(e.Data))
//{
// if (OnReceive != null)
// {
@@ -82,14 +90,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
get
{
- WebSocketState commonState;
-
- if (!Enum.TryParse(WebSocket.ReadyState.ToString(), true, out commonState))
- {
- _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.ReadyState.ToString());
- }
-
- return commonState;
+ return WebSocket.ReadyState;
}
}
@@ -123,7 +124,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs
index 8fb0d4f3e..e75a38e38 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
+++ b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs
@@ -14,9 +14,10 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
-using SocketHttpListener.Primitives;
+using Emby.Server.Implementations.Net;
+using Emby.Server.Implementations.HttpServer;
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
+namespace EmbyServer.SocketSharp
{
public class WebSocketSharpListener : IHttpListener
{
@@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private readonly ILogger _logger;
private readonly X509Certificate _certificate;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly IStreamHelper _streamHelper;
private readonly ITextEncoding _textEncoding;
private readonly INetworkManager _networkManager;
private readonly ISocketFactory _socketFactory;
@@ -36,11 +37,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken;
- public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
+ public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IStreamHelper streamHelper, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
{
_logger = logger;
_certificate = certificate;
- _memoryStreamProvider = memoryStreamProvider;
+ _streamHelper = streamHelper;
_textEncoding = textEncoding;
_networkManager = networkManager;
_socketFactory = socketFactory;
@@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_disposeCancellationToken = _disposeCancellationTokenSource.Token;
}
- public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
+ public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
@@ -62,7 +63,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Start(IEnumerable<string> urlPrefixes)
{
if (_listener == null)
- _listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment);
+ _listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _textEncoding, _streamHelper, _fileSystem, _environment);
_listener.EnableDualMode = _enableDualMode;
@@ -88,6 +89,13 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
Task.Run(() => InitTask(context, _disposeCancellationToken));
}
+ private void LogRequest(ILogger logger, HttpListenerRequest request)
+ {
+ var url = request.Url.ToString();
+
+ logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
+ }
+
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
{
IHttpRequest httpReq = null;
@@ -97,10 +105,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
if (request.IsWebSocketRequest)
{
- LoggerUtils.LogRequest(_logger, request);
+ LogRequest(_logger, request);
- ProcessWebSocketRequest(context);
- return Task.FromResult(true);
+ return ProcessWebSocketRequest(context);
}
httpReq = GetRequest(context);
@@ -110,8 +117,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_logger.ErrorException("Error processing request", ex);
httpReq = httpReq ?? GetRequest(context);
- ErrorHandler(ex, httpReq, true);
- return Task.FromResult(true);
+ return ErrorHandler(ex, httpReq, true, true);
}
var uri = request.Url;
@@ -119,17 +125,19 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return RequestHandler(httpReq, uri.OriginalString, uri.Host, uri.LocalPath, cancellationToken);
}
- private void ProcessWebSocketRequest(HttpListenerContext ctx)
+ private async Task ProcessWebSocketRequest(HttpListenerContext ctx)
{
try
{
var endpoint = ctx.Request.RemoteEndPoint.ToString();
var url = ctx.Request.RawUrl;
+ var queryString = ctx.Request.QueryString;
+
var connectingArgs = new WebSocketConnectingEventArgs
{
Url = url,
- QueryString = ctx.Request.QueryString,
+ QueryString = queryString,
Endpoint = endpoint
};
@@ -142,17 +150,21 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
_logger.Debug("Web socket connection allowed");
- var webSocketContext = ctx.AcceptWebSocket(null);
+ var webSocketContext = await ctx.AcceptWebSocketAsync(null).ConfigureAwait(false);
if (WebSocketConnected != null)
{
+ var socket = new SharpWebSocket(webSocketContext.WebSocket, _logger);
+
WebSocketConnected(new WebSocketConnectEventArgs
{
Url = url,
- QueryString = ctx.Request.QueryString,
- WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger),
+ QueryString = queryString,
+ WebSocket = socket,
Endpoint = endpoint
});
+
+ await ReceiveWebSocket(ctx, socket).ConfigureAwait(false);
}
}
else
@@ -170,11 +182,41 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
}
}
+ private async Task ReceiveWebSocket(HttpListenerContext ctx, SharpWebSocket socket)
+ {
+ try
+ {
+ await socket.StartReceive().ConfigureAwait(false);
+ }
+ finally
+ {
+ TryClose(ctx, 200);
+ }
+ }
+
+ private void TryClose(HttpListenerContext ctx, int statusCode)
+ {
+ try
+ {
+ ctx.Response.StatusCode = 200;
+ ctx.Response.Close();
+ }
+ catch (ObjectDisposedException)
+ {
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error closing web socket response", ex);
+ }
+ }
+
private IHttpRequest GetRequest(HttpListenerContext httpContext)
{
- var operationName = httpContext.Request.GetOperationName();
+ var urlSegments = httpContext.Request.Url.Segments;
+
+ var operationName = urlSegments[urlSegments.Length - 1];
- var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider);
+ var req = new WebSocketSharpRequest(httpContext, operationName, _logger);
return req;
}
@@ -188,7 +230,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_listener.Close();
}
- return Task.FromResult(true);
+ return Task.CompletedTask;
}
public void Dispose()
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs
index 522377f0c..84e37a58c 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
+++ b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using Emby.Server.Implementations.HttpServer;
-using Emby.Server.Implementations.HttpServer.SocketSharp;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
@@ -12,19 +11,18 @@ using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
using IResponse = MediaBrowser.Model.Services.IResponse;
+using System.Threading.Tasks;
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
+namespace EmbyServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
private readonly HttpListenerRequest request;
private readonly IHttpResponse response;
- private readonly IMemoryStreamFactory _memoryStreamProvider;
- public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger, IMemoryStreamFactory memoryStreamProvider)
+ public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger)
{
this.OperationName = operationName;
- _memoryStreamProvider = memoryStreamProvider;
this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
@@ -259,39 +257,20 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
var serverDefaultContentType = "application/json";
var acceptContentTypes = httpReq.AcceptTypes;
- var defaultContentType = httpReq.ContentType;
+ string defaultContentType = null;
if (HasAnyOfContentTypes(httpReq, FormUrlEncoded, MultiPartFormData))
{
defaultContentType = serverDefaultContentType;
}
- var preferredContentTypes = new string[] {};
-
var acceptsAnything = false;
var hasDefaultContentType = !string.IsNullOrEmpty(defaultContentType);
if (acceptContentTypes != null)
{
- var hasPreferredContentTypes = new bool[preferredContentTypes.Length];
foreach (var acceptsType in acceptContentTypes)
{
var contentType = HttpResultFactory.GetRealContentType(acceptsType);
acceptsAnything = acceptsAnything || contentType == "*/*";
-
- for (var i = 0; i < preferredContentTypes.Length; i++)
- {
- if (hasPreferredContentTypes[i]) continue;
- var preferredContentType = preferredContentTypes[i];
- hasPreferredContentTypes[i] = contentType.StartsWith(preferredContentType);
-
- //Prefer Request.ContentType if it is also a preferredContentType
- if (hasPreferredContentTypes[i] && preferredContentType == defaultContentType)
- return preferredContentType;
- }
- }
-
- for (var i = 0; i < preferredContentTypes.Length; i++)
- {
- if (hasPreferredContentTypes[i]) return preferredContentTypes[i];
}
if (acceptsAnything)
@@ -403,8 +382,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return fullPath;
}
-
-
private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot)
{
if (mappedPathRoot == null) return null;
@@ -475,12 +452,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
get { return queryString ?? (queryString = MyHttpUtility.ParseQueryString(request.Url.Query)); }
}
- private QueryParamCollection formData;
- public QueryParamCollection FormData
- {
- get { return formData ?? (formData = this.Form); }
- }
-
public bool IsLocal
{
get { return request.IsLocal; }
@@ -571,23 +542,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
}
}
- static Stream GetSubStream(Stream stream, IMemoryStreamFactory streamProvider)
- {
- if (stream is MemoryStream)
- {
- var other = (MemoryStream)stream;
-
- byte[] buffer;
- if (streamProvider.TryGetBuffer(other, out buffer))
- {
- return streamProvider.CreateNew(buffer);
- }
- return streamProvider.CreateNew(other.ToArray());
- }
-
- return stream;
- }
-
public static string NormalizePathInfo(string pathInfo, string handlerPath)
{
if (handlerPath != null && pathInfo.TrimStart('/').StartsWith(
@@ -599,13 +553,4 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return pathInfo;
}
}
-
- public class HttpFile : IHttpFile
- {
- public string Name { get; set; }
- public string FileName { get; set; }
- public long ContentLength { get; set; }
- public string ContentType { get; set; }
- public Stream InputStream { get; set; }
- }
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs
index 5b51c0cf1..15ff6675d 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
+++ b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs
@@ -8,12 +8,12 @@ using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
-using SocketHttpListener.Net;
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
using IRequest = MediaBrowser.Model.Services.IRequest;
+using System.Net.Sockets;
-namespace Emby.Server.Implementations.HttpServer.SocketSharp
+namespace EmbyServer.SocketSharp
{
public class WebSocketSharpResponse : IHttpResponse
{
@@ -97,33 +97,26 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
try
{
- CloseOutputStream(this._response);
+ var response = this._response;
+
+ var outputStream = response.OutputStream;
+
+ // This is needed with compression
+ outputStream.Flush();
+ outputStream.Dispose();
+
+ response.Close();
+ }
+ catch (SocketException)
+ {
}
catch (Exception ex)
{
- _logger.ErrorException("Error closing HttpListener output stream", ex);
+ _logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex);
}
}
}
- public void CloseOutputStream(HttpListenerResponse response)
- {
- try
- {
- var outputStream = response.OutputStream;
-
- // This is needed with compression
- outputStream.Flush();
- outputStream.Dispose();
-
- response.Close();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex);
- }
- }
-
public bool IsClosed
{
get;
diff --git a/MediaBrowser.Server.Mono/app.config b/MediaBrowser.Server.Mono/app.config
index 62d8a4cae..42a9d04e7 100644
--- a/MediaBrowser.Server.Mono/app.config
+++ b/MediaBrowser.Server.Mono/app.config
@@ -1,31 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8" ?>
<configuration>
- <configSections>
- <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
- </configSections>
- <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <targets async="true"></targets>
- </nlog>
<appSettings>
- <add key="DebugProgramDataPath" value="ProgramData-Server"/>
- <add key="ReleaseProgramDataPath" value="ProgramData-Server"/>
+ <add key="DebugProgramDataPath" value="E:\Temp" />
+ <add key="ReleaseProgramDataPath" value="/var/lib/emby/" />
</appSettings>
- <runtime>
- <legacyUnhandledExceptionPolicy enabled="1"/>
-
- <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
- <dependentAssembly>
- <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral"/>
- <bindingRedirect oldVersion="0.0.0.0-1.0.94.0" newVersion="1.0.94.0"/>
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
- <bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
- </dependentAssembly>
- <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>
+</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config
deleted file mode 100644
index e7aec97b6..000000000
--- a/MediaBrowser.Server.Mono/packages.config
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
- <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
- <package id="SharpCompress" version="0.18.2" targetFramework="net46" />
- <package id="SimpleInjector" version="4.0.12" targetFramework="net46" />
- <package id="SkiaSharp" version="1.58.1" 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/App.config b/MediaBrowser.ServerApplication/App.config
deleted file mode 100644
index d7f4380c4..000000000
--- a/MediaBrowser.ServerApplication/App.config
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
- <configSections>
- <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
-
- <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
- </configSections>
- <system.diagnostics>
- <assert assertuienabled="false" />
- </system.diagnostics>
- <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <targets async="true"></targets>
- </nlog>
- <appSettings>
- <add key="DebugProgramDataPath" value="..\..\..\ProgramData-Server" />
- <add key="ReleaseProgramDataPath" value=".." />
- <add key="ClientSettingsProvider.ServiceUri" value="" />
- </appSettings>
- <startup useLegacyV2RuntimeActivationPolicy="true">
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
- </startup>
- <runtime>
- <gcAllowVeryLargeObjects enabled="true" />
- <gcServer enabled="true" />
- <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
- <dependentAssembly>
- <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Reactive.Core" publicKeyToken="f300afd708cefcd3" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-2.0.20823.0" newVersion="2.0.20823.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Reactive.Interfaces" publicKeyToken="f300afd708cefcd3" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-2.0.20823.0" newVersion="2.0.20823.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-1.5.11.0" newVersion="1.5.11.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-1.5.11.0" newVersion="1.5.11.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-1.0.94.0" newVersion="1.0.94.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-4.0.7.0" newVersion="4.0.7.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
- </dependentAssembly>
- </assemblyBinding>
- <enforceFIPSPolicy enabled="false" />
- </runtime>
- <system.web>
- <membership defaultProvider="ClientAuthenticationMembershipProvider">
- <providers>
- <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
- </providers>
- </membership>
- <roleManager defaultProvider="ClientRoleProvider" enabled="true">
- <providers>
- <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
- </providers>
- </roleManager>
- </system.web>
- <entityFramework>
- <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
- <parameters>
- <parameter value="v11.0" />
- </parameters>
- </defaultConnectionFactory>
- <providers>
- <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
- <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
- </providers>
- </entityFramework>
-
-</configuration>
diff --git a/MediaBrowser.ServerApplication/ApplicationPathHelper.cs b/MediaBrowser.ServerApplication/ApplicationPathHelper.cs
deleted file mode 100644
index e8dad6213..000000000
--- a/MediaBrowser.ServerApplication/ApplicationPathHelper.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.Configuration;
-using System.IO;
-
-namespace MediaBrowser.ServerApplication
-{
- public static class ApplicationPathHelper
- {
- /// <summary>
- /// Gets the path to the application's ProgramDataFolder
- /// </summary>
- /// <returns>System.String.</returns>
- public static string GetProgramDataPath(string applicationPath)
- {
- var useDebugPath = false;
-
-#if DEBUG
- useDebugPath = true;
-#endif
-
- var programDataPath = useDebugPath ?
- ConfigurationManager.AppSettings["DebugProgramDataPath"] :
- ConfigurationManager.AppSettings["ReleaseProgramDataPath"];
-
- programDataPath = programDataPath.Replace("%ApplicationData%", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
-
- programDataPath = programDataPath
- .Replace('/', Path.DirectorySeparatorChar)
- .Replace('\\', Path.DirectorySeparatorChar);
-
- // If it's a relative path, e.g. "..\"
- if (!Path.IsPathRooted(programDataPath))
- {
- var path = Path.GetDirectoryName(applicationPath);
-
- if (string.IsNullOrEmpty(path))
- {
- throw new ApplicationException("Unable to determine running assembly location");
- }
-
- programDataPath = Path.Combine(path, programDataPath);
-
- programDataPath = Path.GetFullPath(programDataPath);
- }
-
- Directory.CreateDirectory(programDataPath);
-
- return programDataPath;
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/BackgroundService.cs b/MediaBrowser.ServerApplication/BackgroundService.cs
deleted file mode 100644
index e90251ae6..000000000
--- a/MediaBrowser.ServerApplication/BackgroundService.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System.ServiceProcess;
-
-namespace MediaBrowser.ServerApplication
-{
- /// <summary>
- /// Class BackgroundService
- /// </summary>
- public class BackgroundService : ServiceBase
- {
- public static string Name = "Emby";
- public static string DisplayName = "Emby Server";
-
- public static string GetExistingServiceName()
- {
- return Name;
- }
-
- private readonly ILogger _logger;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="BackgroundService"/> class.
- /// </summary>
- public BackgroundService(ILogger logger)
- {
- _logger = logger;
-
- CanPauseAndContinue = false;
-
- CanStop = true;
-
- ServiceName = GetExistingServiceName();
- }
-
- /// <summary>
- /// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
- /// </summary>
- protected override void OnStop()
- {
- _logger.Info("Stop command received");
-
- base.OnStop();
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs b/MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs
deleted file mode 100644
index be381fe96..000000000
--- a/MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System.Collections;
-using System.ComponentModel;
-using System.ServiceProcess;
-
-namespace MediaBrowser.ServerApplication
-{
- [RunInstaller(true)]
- public class BackgroundServiceInstaller : System.Configuration.Install.Installer
- {
- public BackgroundServiceInstaller()
- {
- var process = new ServiceProcessInstaller
- {
- Account = ServiceAccount.LocalSystem
- };
-
- var serviceAdmin = new ServiceInstaller
- {
- StartType = ServiceStartMode.Manual,
- ServiceName = BackgroundService.Name,
- DisplayName = BackgroundService.DisplayName,
-
- DelayedAutoStart = true,
-
- Description = "The windows background service for Emby Server.",
-
- // Will ensure the network is available
- ServicesDependedOn = new[] { "LanmanServer", "EventLog", "Tcpip", "http" }
- };
-
- // Microsoft didn't add the ability to add a
- // description for the services we are going to install
- // To work around this we'll have to add the
- // information directly to the registry but I'll leave
- // this exercise for later.
-
- // now just add the installers that we created to our
- // parents container, the documentation
- // states that there is not any order that you need to
- // worry about here but I'll still
- // go ahead and add them in the order that makes sense.
- Installers.Add(process);
- Installers.Add(serviceAdmin);
- }
-
- protected override void OnBeforeInstall(IDictionary savedState)
- {
- Context.Parameters["assemblypath"] = "\"" +
- Context.Parameters["assemblypath"] + "\" " + GetStartArgs();
- base.OnBeforeInstall(savedState);
- }
-
- protected override void OnBeforeUninstall(IDictionary savedState)
- {
- Context.Parameters["assemblypath"] = "\"" +
- Context.Parameters["assemblypath"] + "\" " + GetStartArgs();
- base.OnBeforeUninstall(savedState);
- }
-
- private string GetStartArgs()
- {
- return "-service";
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Icon.ico b/MediaBrowser.ServerApplication/Icon.ico
deleted file mode 100644
index 0abd554f4..000000000
--- a/MediaBrowser.ServerApplication/Icon.ico
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.ServerApplication/ImageEncoderHelper.cs b/MediaBrowser.ServerApplication/ImageEncoderHelper.cs
deleted file mode 100644
index 7c95a25de..000000000
--- a/MediaBrowser.ServerApplication/ImageEncoderHelper.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using Emby.Drawing;
-using Emby.Drawing.Skia;
-using Emby.Server.Implementations;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Startup.Common
-{
- public class ImageEncoderHelper
- {
- public static IImageEncoder GetImageEncoder(ILogger logger,
- ILogManager logManager,
- IFileSystem fileSystem,
- StartupOptions startupOptions,
- Func<IHttpClient> httpClient,
- IApplicationPaths appPaths,
- ILocalizationManager localizationManager)
- {
- try
- {
- return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem, localizationManager);
- }
- catch
- {
- logger.Error("Skia not available. Will try next image processor.");
- }
-
- return new NullImageEncoder();
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
deleted file mode 100644
index 91a949921..000000000
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ /dev/null
@@ -1,800 +0,0 @@
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.ServerApplication.Native;
-using MediaBrowser.ServerApplication.Splash;
-using MediaBrowser.ServerApplication.Updates;
-using Microsoft.Win32;
-using System;
-using System.Configuration.Install;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Management;
-using System.Runtime.InteropServices;
-using System.ServiceProcess;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using Emby.Drawing;
-using Emby.Server.Implementations;
-using Emby.Server.Implementations.Browser;
-using Emby.Server.Implementations.EnvironmentInfo;
-using Emby.Server.Implementations.IO;
-using Emby.Server.Implementations.Logging;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Model.IO;
-using SystemEvents = Emby.Server.Implementations.SystemEvents;
-
-namespace MediaBrowser.ServerApplication
-{
- public class MainStartup
- {
- private static IServerApplicationPaths _appPaths;
- private static ILogManager _logManager;
-
- private static ILogger _logger;
-
- public static bool IsRunningAsService = false;
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool SetDllDirectory(string lpPathName);
-
- public static string ApplicationPath;
-
- private static IFileSystem FileSystem;
- private static bool _restartOnShutdown;
-
- /// <summary>
- /// Defines the entry point of the application.
- /// </summary>
- [STAThread]
- public static void Main()
- {
- var options = new StartupOptions(Environment.GetCommandLineArgs());
- IsRunningAsService = options.ContainsOption("-service");
-
- var currentProcess = Process.GetCurrentProcess();
-
- ApplicationPath = currentProcess.MainModule.FileName;
- var architecturePath = Path.Combine(Path.GetDirectoryName(ApplicationPath), Environment.Is64BitProcess ? "x64" : "x86");
-
- var success = SetDllDirectory(architecturePath);
-
- SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
-
- var appPaths = CreateApplicationPaths(ApplicationPath, IsRunningAsService);
- _appPaths = appPaths;
-
- using (var logManager = new SimpleLogManager(appPaths.LogDirectoryPath, "server"))
- {
- _logManager = logManager;
-
- logManager.ReloadLogger(LogSeverity.Debug);
- logManager.AddConsoleOutput();
-
- var logger = _logger = logManager.GetLogger("Main");
-
- ApplicationHost.LogEnvironmentInfo(logger, appPaths, true);
-
- // Uninstall directly
- if (options.ContainsOption("-uninstallservice"))
- {
- logger.Info("Performing service uninstallation");
- UninstallService(ApplicationPath, logger);
- return;
- }
-
- // Restart with admin rights, then uninstall
- if (options.ContainsOption("-uninstallserviceasadmin"))
- {
- logger.Info("Performing service uninstallation");
- RunServiceUninstallation(ApplicationPath);
- return;
- }
-
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-
- if (IsAlreadyRunning(ApplicationPath, currentProcess))
- {
- logger.Info("Shutting down because another instance of Emby Server is already running.");
- return;
- }
-
- if (PerformUpdateIfNeeded(appPaths, logger))
- {
- logger.Info("Exiting to perform application update.");
- return;
- }
-
- RunApplication(appPaths, logManager, IsRunningAsService, options);
-
- logger.Info("Shutdown complete");
-
- if (_restartOnShutdown)
- {
- logger.Info("Starting new server process");
- var restartCommandLine = GetRestartCommandLine();
-
- Process.Start(restartCommandLine.Item1, restartCommandLine.Item2);
- }
- }
- }
-
- public static Tuple<string, string> GetRestartCommandLine()
- {
- var currentProcess = Process.GetCurrentProcess();
- var processModulePath = currentProcess.MainModule.FileName;
-
- return new Tuple<string, string>(processModulePath, Environment.CommandLine);
- }
-
- private static bool IsServiceInstalled()
- {
- try
- {
- var serviceName = BackgroundService.GetExistingServiceName();
- var ctl = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == serviceName);
-
- return ctl != null;
- }
- catch
- {
- return false;
- }
- }
-
- /// <summary>
- /// Determines whether [is already running] [the specified current process].
- /// </summary>
- /// <param name="applicationPath">The application path.</param>
- /// <param name="currentProcess">The current process.</param>
- /// <returns><c>true</c> if [is already running] [the specified current process]; otherwise, <c>false</c>.</returns>
- private static bool IsAlreadyRunning(string applicationPath, Process currentProcess)
- {
- var duplicate = Process.GetProcesses().FirstOrDefault(i =>
- {
- try
- {
- if (currentProcess.Id == i.Id)
- {
- return false;
- }
- }
- catch (Exception)
- {
- return false;
- }
-
- try
- {
- //_logger.Info("Module: {0}", i.MainModule.FileName);
- if (string.Equals(applicationPath, i.MainModule.FileName, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- return false;
- }
- catch (Exception)
- {
- return false;
- }
- });
-
- if (duplicate != null)
- {
- _logger.Info("Found a duplicate process. Giving it time to exit.");
-
- if (!duplicate.WaitForExit(40000))
- {
- _logger.Info("The duplicate process did not exit.");
- return true;
- }
- }
-
- if (!IsRunningAsService)
- {
- return IsAlreadyRunningAsService(applicationPath);
- }
-
- return false;
- }
-
- private static bool IsAlreadyRunningAsService(string applicationPath)
- {
- try
- {
- var serviceName = BackgroundService.GetExistingServiceName();
-
- WqlObjectQuery wqlObjectQuery = new WqlObjectQuery(string.Format("SELECT * FROM Win32_Service WHERE State = 'Running' AND Name = '{0}'", serviceName));
- ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(wqlObjectQuery);
- ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
-
- foreach (ManagementObject managementObject in managementObjectCollection)
- {
- var obj = managementObject.GetPropertyValue("PathName");
- if (obj == null)
- {
- continue;
- }
- var path = obj.ToString();
-
- _logger.Info("Service path: {0}", path);
- // Need to use indexOf instead of equality because the path will have the full service command line
- if (path.IndexOf(applicationPath, StringComparison.OrdinalIgnoreCase) != -1)
- {
- _logger.Info("The windows service is already running");
- MessageBox.Show("Emby Server is already running as a Windows Service. Only one instance is allowed at a time. To run as a tray icon, shut down the Windows Service.");
- return true;
- }
- }
- }
- catch (COMException)
- {
- // Catch errors thrown due to WMI not being initialized
- }
-
- return false;
- }
-
- /// <summary>
- /// Creates the application paths.
- /// </summary>
- /// <param name="applicationPath">The application path.</param>
- /// <param name="runAsService">if set to <c>true</c> [run as service].</param>
- /// <returns>ServerApplicationPaths.</returns>
- private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, bool runAsService)
- {
- var appFolderPath = Path.GetDirectoryName(applicationPath);
-
- var resourcesPath = Path.GetDirectoryName(applicationPath);
-
- if (runAsService && IsServiceInstalled())
- {
- var systemPath = Path.GetDirectoryName(applicationPath);
-
- var programDataPath = Path.GetDirectoryName(systemPath);
-
- return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
- }
-
- return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), appFolderPath, resourcesPath);
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance can self restart.
- /// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public static bool CanSelfRestart
- {
- get
- {
- if (IsRunningAsService)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public static bool CanSelfUpdate
- {
- get
- {
-#if DEBUG
- return false;
-#endif
-
- if (IsRunningAsService)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
-
- private static string UpdatePackageFileName
- {
- get
- {
- if (Environment.Is64BitOperatingSystem)
- {
- return "embyserver-win-x64-{version}.zip";
- }
-
- return "embyserver-win-x86-{version}.zip";
- }
- }
-
- /// <summary>
- /// Runs the application.
- /// </summary>
- /// <param name="appPaths">The app paths.</param>
- /// <param name="logManager">The log manager.</param>
- /// <param name="runService">if set to <c>true</c> [run service].</param>
- /// <param name="options">The options.</param>
- private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
- {
- var environmentInfo = new EnvironmentInfo();
-
- var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, appPaths.TempDirectory);
-
- FileSystem = fileSystem;
-
- using (var appHost = new WindowsAppHost(appPaths,
- logManager,
- options,
- fileSystem,
- new PowerManagement(),
- UpdatePackageFileName,
- environmentInfo,
- new NullImageEncoder(),
- new SystemEvents(logManager.GetLogger("SystemEvents")),
- new Networking.NetworkManager(logManager.GetLogger("NetworkManager"))))
- {
- var initProgress = new Progress<double>();
-
- if (!runService)
- {
- if (!options.ContainsOption("-nosplash")) ShowSplashScreen(appHost.ApplicationVersion, initProgress, logManager.GetLogger("Splash"));
-
- // Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes
- SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT |
- ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX);
- }
-
- var task = appHost.Init(initProgress);
- Task.WaitAll(task);
-
- if (!runService)
- {
- task = InstallVcredist2013IfNeeded(appHost.HttpClient, _logger);
- Task.WaitAll(task);
-
- // needed by skia
- task = InstallVcredist2015IfNeeded(appHost.HttpClient, _logger);
- Task.WaitAll(task);
- }
-
- // set image encoder here
- appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, appHost.LocalizationManager);
-
- task = task.ContinueWith(new Action<Task>(a => appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
-
- if (runService && IsServiceInstalled())
- {
- StartService(logManager);
- }
- else
- {
- Task.WaitAll(task);
-
- HideSplashScreen();
-
- ShowTrayIcon(appHost);
- }
- }
- }
-
- private static ServerNotifyIcon _serverNotifyIcon;
- private static TaskScheduler _mainTaskScheduler;
- private static void ShowTrayIcon(ApplicationHost appHost)
- {
- //Application.EnableVisualStyles();
- //Application.SetCompatibleTextRenderingDefault(false);
- _serverNotifyIcon = new ServerNotifyIcon(appHost.LogManager, appHost, appHost.ServerConfigurationManager, appHost.LocalizationManager);
- _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
- Application.Run();
- }
-
- internal static SplashForm _splash;
- private static Thread _splashThread;
- private static void ShowSplashScreen(Version appVersion, Progress<double> progress, ILogger logger)
- {
- var thread = new Thread(() =>
- {
- _splash = new SplashForm(appVersion, progress);
-
- _splash.ShowDialog();
- });
-
- thread.SetApartmentState(ApartmentState.STA);
- thread.IsBackground = true;
- thread.Start();
-
- _splashThread = thread;
- }
-
- private static void HideSplashScreen()
- {
- if (_splash != null)
- {
- Action act = () =>
- {
- _splash.Close();
- _splashThread = null;
- };
-
- _splash.Invoke(act);
- }
- }
-
- public static void Invoke(Action action)
- {
- if (IsRunningAsService)
- {
- action();
- }
- else
- {
- Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _mainTaskScheduler ?? TaskScheduler.Current);
- }
- }
-
- /// <summary>
- /// Starts the service.
- /// </summary>
- private static void StartService(ILogManager logManager)
- {
- var service = new BackgroundService(logManager.GetLogger("Service"));
-
- ServiceBase.Run(service);
- }
-
- /// <summary>
- /// Uninstalls the service.
- /// </summary>
- private static void UninstallService(string applicationPath, ILogger logger)
- {
- try
- {
- ManagedInstallerClass.InstallHelper(new[] { "/u", applicationPath });
-
- logger.Info("Service uninstallation succeeded");
- }
- catch (Exception ex)
- {
- logger.ErrorException("Uninstall failed", ex);
- }
- }
-
- /// <summary>
- /// Runs the service uninstallation.
- /// </summary>
- private static void RunServiceUninstallation(string applicationPath)
- {
- var startInfo = new ProcessStartInfo
- {
- FileName = applicationPath,
-
- Arguments = "-uninstallservice",
-
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas",
- ErrorDialog = false
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.WaitForExit();
- }
- }
-
- /// <summary>
- /// Handles the UnhandledException event of the CurrentDomain control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="UnhandledExceptionEventArgs"/> instance containing the event data.</param>
- static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- var exception = (Exception)e.ExceptionObject;
-
- new UnhandledExceptionWriter(_appPaths, _logger, _logManager, FileSystem, new ConsoleLogger()).Log(exception);
-
- if (!IsRunningAsService)
- {
- MessageBox.Show("Unhandled exception: " + exception.Message);
- }
-
- if (!Debugger.IsAttached)
- {
- Environment.Exit(Marshal.GetHRForException(exception));
- }
- }
-
- /// <summary>
- /// Performs the update if needed.
- /// </summary>
- /// <param name="appPaths">The app paths.</param>
- /// <param name="logger">The logger.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger)
- {
- // Not supported
- if (IsRunningAsService)
- {
- return false;
- }
-
- // Look for the existence of an update archive
- var updateArchive = Path.Combine(appPaths.TempUpdatePath, "MBServer" + ".zip");
- if (File.Exists(updateArchive))
- {
- logger.Info("An update is available from {0}", updateArchive);
-
- // Update is there - execute update
- try
- {
- var serviceName = IsRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty;
- new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName);
-
- // And just let the app exit so it can update
- return true;
- }
- catch (Exception e)
- {
- logger.ErrorException("Error starting updater.", e);
-
- MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
- }
- }
-
- return false;
- }
-
- public static void Shutdown()
- {
- if (IsRunningAsService && IsServiceInstalled())
- {
- ShutdownWindowsService();
- }
- else
- {
- ShutdownWindowsApplication();
- }
- }
-
- public static void Restart()
- {
- if (IsRunningAsService)
- {
- }
- else
- {
- _restartOnShutdown = true;
- ShutdownWindowsApplication();
- }
- }
-
- private static void ShutdownWindowsApplication()
- {
- if (_serverNotifyIcon != null)
- {
- _serverNotifyIcon.Dispose();
- _serverNotifyIcon = null;
- }
-
- _logger.Info("Calling Application.Exit");
- Application.Exit();
- }
-
- private static void ShutdownWindowsService()
- {
- _logger.Info("Stopping background service");
- var service = new ServiceController(BackgroundService.GetExistingServiceName());
-
- service.Refresh();
-
- if (service.Status == ServiceControllerStatus.Running)
- {
- service.Stop();
- }
- }
-
- private static async Task InstallVcredist2013IfNeeded(IHttpClient httpClient, ILogger logger)
- {
- // Reference
- // http://stackoverflow.com/questions/12206314/detect-if-visual-c-redistributable-for-visual-studio-2012-is-installed
-
- try
- {
- var subkey = Environment.Is64BitProcess
- ? "SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\12.0\\VC\\Runtimes\\x64"
- : "SOFTWARE\\Microsoft\\VisualStudio\\12.0\\VC\\Runtimes\\x86";
-
- using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey(subkey))
- {
- if (ndpKey != null && ndpKey.GetValue("Version") != null)
- {
- var installedVersion = ((string)ndpKey.GetValue("Version")).TrimStart('v');
- if (installedVersion.StartsWith("12", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error getting .NET Framework version", ex);
- return;
- }
-
- try
- {
- await InstallVcredist(GetVcredist2013Url(), httpClient).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error installing Visual Studio C++ runtime", ex);
- }
- }
-
- private static string GetVcredist2013Url()
- {
- if (Environment.Is64BitProcess)
- {
- return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_x64.exe";
- }
-
- // TODO: ARM url - https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_arm.exe
-
- return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_x86.exe";
- }
-
- private static async Task InstallVcredist2015IfNeeded(IHttpClient httpClient, ILogger logger)
- {
- // Reference
- // http://stackoverflow.com/questions/12206314/detect-if-visual-c-redistributable-for-visual-studio-2012-is-installed
-
- try
- {
- RegistryKey key;
-
- if (Environment.Is64BitProcess)
- {
- key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey("SOFTWARE\\Classes\\Installer\\Dependencies\\{d992c12e-cab2-426f-bde3-fb8c53950b0d}");
-
- if (key == null)
- {
- key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64");
- }
- }
- else
- {
- key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey("SOFTWARE\\Classes\\Installer\\Dependencies\\{e2803110-78b3-4664-a479-3611a381656a}");
-
- if (key == null)
- {
- key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey("SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86");
- }
- }
-
- if (key != null)
- {
- using (key)
- {
- var version = key.GetValue("Version");
- if (version != null)
- {
- var installedVersion = ((string)version).TrimStart('v');
- if (installedVersion.StartsWith("14", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error getting .NET Framework version", ex);
- return;
- }
-
- try
- {
- await InstallVcredist(GetVcredist2015Url(), httpClient).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error installing Visual Studio C++ runtime", ex);
- }
- }
-
- private static string GetVcredist2015Url()
- {
- if (Environment.Is64BitProcess)
- {
- return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2015/vc_redist.x64.exe";
- }
-
- // TODO: ARM url - https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2015/vcredist_arm.exe
-
- return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2015/vc_redist.x86.exe";
- }
-
- private async static Task InstallVcredist(string url, IHttpClient httpClient)
- {
- var tmp = await httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- Progress = new Progress<double>()
-
- }).ConfigureAwait(false);
-
- var exePath = Path.ChangeExtension(tmp, ".exe");
- File.Copy(tmp, exePath);
-
- var startInfo = new ProcessStartInfo
- {
- FileName = exePath,
-
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas",
- ErrorDialog = false
- };
-
- _logger.Info("Running {0}", startInfo.FileName);
-
- using (var process = Process.Start(startInfo))
- {
- process.WaitForExit();
- }
- }
-
- /// <summary>
- /// Sets the error mode.
- /// </summary>
- /// <param name="uMode">The u mode.</param>
- /// <returns>ErrorModes.</returns>
- [DllImport("kernel32.dll")]
- static extern ErrorModes SetErrorMode(ErrorModes uMode);
-
- /// <summary>
- /// Enum ErrorModes
- /// </summary>
- [Flags]
- public enum ErrorModes : uint
- {
- /// <summary>
- /// The SYSTE m_ DEFAULT
- /// </summary>
- SYSTEM_DEFAULT = 0x0,
- /// <summary>
- /// The SE m_ FAILCRITICALERRORS
- /// </summary>
- SEM_FAILCRITICALERRORS = 0x0001,
- /// <summary>
- /// The SE m_ NOALIGNMENTFAULTEXCEPT
- /// </summary>
- SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
- /// <summary>
- /// The SE m_ NOGPFAULTERRORBOX
- /// </summary>
- SEM_NOGPFAULTERRORBOX = 0x0002,
- /// <summary>
- /// The SE m_ NOOPENFILEERRORBOX
- /// </summary>
- SEM_NOOPENFILEERRORBOX = 0x8000
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
deleted file mode 100644
index f479db46f..000000000
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ /dev/null
@@ -1,279 +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>{94ADE4D3-B7EC-45CD-A200-CC469433072B}</ProjectGuid>
- <OutputType>WinExe</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.ServerApplication</RootNamespace>
- <AssemblyName>MediaBrowser.ServerApplication</AssemblyName>
- <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <TargetFrameworkProfile />
- <NuGetPackageImportStamp>
- </NuGetPackageImportStamp>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <PlatformTarget>AnyCPU</PlatformTarget>
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <PlatformTarget>AnyCPU</PlatformTarget>
- <DebugType>none</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup>
- <StartupObject>MediaBrowser.ServerApplication.MainStartup</StartupObject>
- </PropertyGroup>
- <PropertyGroup>
- <ApplicationIcon>Icon.ico</ApplicationIcon>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
- <DebugSymbols>true</DebugSymbols>
- <OutputPath>bin\x86\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <DebugType>full</DebugType>
- <PlatformTarget>x86</PlatformTarget>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
- <Prefer32Bit>true</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
- <OutputPath>bin\x86\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <Optimize>true</Optimize>
- <DebugType>none</DebugType>
- <PlatformTarget>x86</PlatformTarget>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
- <Prefer32Bit>true</Prefer32Bit>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="Emby.Server.CinemaMode">
- <HintPath>..\ThirdParty\emby\Emby.Server.CinemaMode.dll</HintPath>
- </Reference>
- <Reference Include="Emby.Server.Connect">
- <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
- </Reference>
- <Reference Include="Emby.Server.Sync">
- <HintPath>..\ThirdParty\emby\Emby.Server.Sync.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="SharpCompress, Version=0.18.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
- <HintPath>..\packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll</HintPath>
- </Reference>
- <Reference Include="SimpleInjector, Version=4.0.12.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.4.0.12\lib\net45\SimpleInjector.dll</HintPath>
- </Reference>
- <Reference Include="SkiaSharp, Version=1.58.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
- <HintPath>..\packages\SkiaSharp.1.58.1\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.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.8\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Configuration" />
- <Reference Include="System.Configuration.Install" />
- <Reference Include="System.Core" />
- <Reference Include="System.Drawing" />
- <Reference Include="System.IO.Compression" />
- <Reference Include="System.Management" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="System.ServiceProcess" />
- <Reference Include="System.Windows.Forms" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="ApplicationPathHelper.cs" />
- <Compile Include="BackgroundService.cs">
- <SubType>Component</SubType>
- </Compile>
- <Compile Include="BackgroundServiceInstaller.cs">
- <SubType>Component</SubType>
- </Compile>
- <Compile Include="ImageEncoderHelper.cs" />
- <Compile Include="MainStartup.cs" />
- <Compile Include="Native\LnkShortcutHandler.cs" />
- <Compile Include="Native\LoopUtil.cs" />
- <Compile Include="Native\PowerManagement.cs" />
- <Compile Include="Native\Standby.cs" />
- <Compile Include="Native\ServerAuthorization.cs" />
- <Compile Include="Networking\NativeMethods.cs" />
- <Compile Include="Networking\NetworkManager.cs" />
- <Compile Include="Networking\NetworkShares.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Properties\Resources.Designer.cs">
- <AutoGen>True</AutoGen>
- <DesignTime>True</DesignTime>
- <DependentUpon>Resources.resx</DependentUpon>
- </Compile>
- <Compile Include="ServerNotifyIcon.cs" />
- <Compile Include="Splash\SplashForm.cs">
- <SubType>Form</SubType>
- </Compile>
- <Compile Include="Splash\SplashForm.Designer.cs">
- <DependentUpon>SplashForm.cs</DependentUpon>
- </Compile>
- <Compile Include="Updates\ApplicationUpdater.cs" />
- <Compile Include="WindowsAppHost.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="App.config" />
- <None Include="app.manifest" />
- <EmbeddedResource Include="Native\RegisterServer.bat" />
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Properties\Resources.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resources.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Include="Splash\SplashForm.resx">
- <DependentUpon>SplashForm.cs</DependentUpon>
- </EmbeddedResource>
- </ItemGroup>
- <ItemGroup>
- <Content Include="..\packages\SkiaSharp.1.58.1\runtimes\win7-x64\native\libSkiaSharp.dll">
- <Link>x64\libSkiaSharp.dll</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\packages\SkiaSharp.1.58.1\runtimes\win7-x86\native\libSkiaSharp.dll">
- <Link>x86\libSkiaSharp.dll</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\Tools\Installation\MediaBrowser.InstallUtil.dll">
- <Link>MediaBrowser.InstallUtil.dll</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\Tools\Installation\MediaBrowser.Uninstaller.exe">
- <Link>MediaBrowser.Uninstaller.exe</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\Tools\Installation\MediaBrowser.Updater.exe">
- <Link>MediaBrowser.Updater.exe</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="x64\sqlite3.dll">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <EmbeddedResource Include="Icon.ico" />
- <Content Include="Resources\Images\mb3logo800.png" />
- <Content Include="x86\sqlite3.dll">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\BDInfo\BDInfo.csproj">
- <Project>{88ae38df-19d7-406f-a6a9-09527719a21e}</Project>
- <Name>BDInfo</Name>
- </ProjectReference>
- <ProjectReference Include="..\DvdLib\DvdLib.csproj">
- <Project>{713f42b5-878e-499d-a878-e4c652b1d5e8}</Project>
- <Name>DvdLib</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.Skia\Emby.Drawing.Skia.csproj">
- <Project>{2312da6d-ff86-4597-9777-bceec32d96dd}</Project>
- <Name>Emby.Drawing.Skia</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.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.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="..\RSSDP\RSSDP.csproj">
- <Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
- <Name>RSSDP</Name>
- </ProjectReference>
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <PropertyGroup>
- <PostBuildEvent>
- </PostBuildEvent>
- </PropertyGroup>
- <!-- 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.ServerApplication/Native/LnkShortcutHandler.cs b/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
deleted file mode 100644
index e53a79670..000000000
--- a/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
+++ /dev/null
@@ -1,332 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.ComTypes;
-using System.Text;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- public class LnkShortcutHandler :IShortcutHandler
- {
- public string Extension
- {
- get { return ".lnk"; }
- }
-
- public string Resolve(string shortcutPath)
- {
- var link = new ShellLink();
- ((IPersistFile)link).Load(shortcutPath, NativeMethods.STGM_READ);
- // ((IShellLinkW)link).Resolve(hwnd, 0)
- var sb = new StringBuilder(NativeMethods.MAX_PATH);
- WIN32_FIND_DATA data;
- ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0);
- return sb.ToString();
- }
-
- public void Create(string shortcutPath, string targetPath)
- {
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// Class NativeMethods
- /// </summary>
- public static class NativeMethods
- {
- /// <summary>
- /// The MA x_ PATH
- /// </summary>
- public const int MAX_PATH = 260;
- /// <summary>
- /// The MA x_ ALTERNATE
- /// </summary>
- public const int MAX_ALTERNATE = 14;
- /// <summary>
- /// The INVALI d_ HANDL e_ VALUE
- /// </summary>
- public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
- /// <summary>
- /// The STG m_ READ
- /// </summary>
- public const int STGM_READ = 0;
- }
-
- /// <summary>
- /// Struct FILETIME
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct FILETIME
- {
- /// <summary>
- /// The dw low date time
- /// </summary>
- public uint dwLowDateTime;
- /// <summary>
- /// The dw high date time
- /// </summary>
- public uint dwHighDateTime;
- }
-
- /// <summary>
- /// Struct WIN32_FIND_DATA
- /// </summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct WIN32_FIND_DATA
- {
- /// <summary>
- /// The dw file attributes
- /// </summary>
- public FileAttributes dwFileAttributes;
- /// <summary>
- /// The ft creation time
- /// </summary>
- public FILETIME ftCreationTime;
- /// <summary>
- /// The ft last access time
- /// </summary>
- public FILETIME ftLastAccessTime;
- /// <summary>
- /// The ft last write time
- /// </summary>
- public FILETIME ftLastWriteTime;
- /// <summary>
- /// The n file size high
- /// </summary>
- public int nFileSizeHigh;
- /// <summary>
- /// The n file size low
- /// </summary>
- public int nFileSizeLow;
- /// <summary>
- /// The dw reserved0
- /// </summary>
- public int dwReserved0;
- /// <summary>
- /// The dw reserved1
- /// </summary>
- public int dwReserved1;
-
- /// <summary>
- /// The c file name
- /// </summary>
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_PATH)]
- public string cFileName;
-
- /// <summary>
- /// This will always be null when FINDEX_INFO_LEVELS = basic
- /// </summary>
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_ALTERNATE)]
- public string cAlternate;
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Returns a <see cref="System.String" /> that represents this instance.
- /// </summary>
- /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
- public override string ToString()
- {
- return Path ?? string.Empty;
- }
- }
-
- /// <summary>
- /// Enum SLGP_FLAGS
- /// </summary>
- [Flags]
- public enum SLGP_FLAGS
- {
- /// <summary>
- /// Retrieves the standard short (8.3 format) file name
- /// </summary>
- SLGP_SHORTPATH = 0x1,
- /// <summary>
- /// Retrieves the Universal Naming Convention (UNC) path name of the file
- /// </summary>
- SLGP_UNCPRIORITY = 0x2,
- /// <summary>
- /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded
- /// </summary>
- SLGP_RAWPATH = 0x4
- }
- /// <summary>
- /// Enum SLR_FLAGS
- /// </summary>
- [Flags]
- public enum SLR_FLAGS
- {
- /// <summary>
- /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set,
- /// the high-order word of fFlags can be set to a time-out value that specifies the
- /// maximum amount of time to be spent resolving the link. The function returns if the
- /// link cannot be resolved within the time-out duration. If the high-order word is set
- /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds
- /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out
- /// duration, in milliseconds.
- /// </summary>
- SLR_NO_UI = 0x1,
- /// <summary>
- /// Obsolete and no longer used
- /// </summary>
- SLR_ANY_MATCH = 0x2,
- /// <summary>
- /// If the link object has changed, update its path and list of identifiers.
- /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine
- /// whether or not the link object has changed.
- /// </summary>
- SLR_UPDATE = 0x4,
- /// <summary>
- /// Do not update the link information
- /// </summary>
- SLR_NOUPDATE = 0x8,
- /// <summary>
- /// Do not execute the search heuristics
- /// </summary>
- SLR_NOSEARCH = 0x10,
- /// <summary>
- /// Do not use distributed link tracking
- /// </summary>
- SLR_NOTRACK = 0x20,
- /// <summary>
- /// Disable distributed link tracking. By default, distributed link tracking tracks
- /// removable media across multiple devices based on the volume name. It also uses the
- /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter
- /// has changed. Setting SLR_NOLINKINFO disables both types of tracking.
- /// </summary>
- SLR_NOLINKINFO = 0x40,
- /// <summary>
- /// Call the Microsoft Windows Installer
- /// </summary>
- SLR_INVOKE_MSI = 0x80
- }
-
- /// <summary>
- /// The IShellLink interface allows Shell links to be created, modified, and resolved
- /// </summary>
- [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
- public interface IShellLinkW
- {
- /// <summary>
- /// Retrieves the path and file name of a Shell link object
- /// </summary>
- /// <param name="pszFile">The PSZ file.</param>
- /// <param name="cchMaxPath">The CCH max path.</param>
- /// <param name="pfd">The PFD.</param>
- /// <param name="fFlags">The f flags.</param>
- void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATA pfd, SLGP_FLAGS fFlags);
- /// <summary>
- /// Retrieves the list of item identifiers for a Shell link object
- /// </summary>
- /// <param name="ppidl">The ppidl.</param>
- void GetIDList(out IntPtr ppidl);
- /// <summary>
- /// Sets the pointer to an item identifier list (PIDL) for a Shell link object.
- /// </summary>
- /// <param name="pidl">The pidl.</param>
- void SetIDList(IntPtr pidl);
- /// <summary>
- /// Retrieves the description string for a Shell link object
- /// </summary>
- /// <param name="pszName">Name of the PSZ.</param>
- /// <param name="cchMaxName">Name of the CCH max.</param>
- void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
- /// <summary>
- /// Sets the description for a Shell link object. The description can be any application-defined string
- /// </summary>
- /// <param name="pszName">Name of the PSZ.</param>
- void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
- /// <summary>
- /// Retrieves the name of the working directory for a Shell link object
- /// </summary>
- /// <param name="pszDir">The PSZ dir.</param>
- /// <param name="cchMaxPath">The CCH max path.</param>
- void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
- /// <summary>
- /// Sets the name of the working directory for a Shell link object
- /// </summary>
- /// <param name="pszDir">The PSZ dir.</param>
- void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
- /// <summary>
- /// Retrieves the command-line arguments associated with a Shell link object
- /// </summary>
- /// <param name="pszArgs">The PSZ args.</param>
- /// <param name="cchMaxPath">The CCH max path.</param>
- void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
- /// <summary>
- /// Sets the command-line arguments for a Shell link object
- /// </summary>
- /// <param name="pszArgs">The PSZ args.</param>
- void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
- /// <summary>
- /// Retrieves the hot key for a Shell link object
- /// </summary>
- /// <param name="pwHotkey">The pw hotkey.</param>
- void GetHotkey(out short pwHotkey);
- /// <summary>
- /// Sets a hot key for a Shell link object
- /// </summary>
- /// <param name="wHotkey">The w hotkey.</param>
- void SetHotkey(short wHotkey);
- /// <summary>
- /// Retrieves the show command for a Shell link object
- /// </summary>
- /// <param name="piShowCmd">The pi show CMD.</param>
- void GetShowCmd(out int piShowCmd);
- /// <summary>
- /// Sets the show command for a Shell link object. The show command sets the initial show state of the window.
- /// </summary>
- /// <param name="iShowCmd">The i show CMD.</param>
- void SetShowCmd(int iShowCmd);
- /// <summary>
- /// Retrieves the location (path and index) of the icon for a Shell link object
- /// </summary>
- /// <param name="pszIconPath">The PSZ icon path.</param>
- /// <param name="cchIconPath">The CCH icon path.</param>
- /// <param name="piIcon">The pi icon.</param>
- void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
- int cchIconPath, out int piIcon);
- /// <summary>
- /// Sets the location (path and index) of the icon for a Shell link object
- /// </summary>
- /// <param name="pszIconPath">The PSZ icon path.</param>
- /// <param name="iIcon">The i icon.</param>
- void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
- /// <summary>
- /// Sets the relative path to the Shell link object
- /// </summary>
- /// <param name="pszPathRel">The PSZ path rel.</param>
- /// <param name="dwReserved">The dw reserved.</param>
- void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
- /// <summary>
- /// Attempts to find the target of a Shell link, even if it has been moved or renamed
- /// </summary>
- /// <param name="hwnd">The HWND.</param>
- /// <param name="fFlags">The f flags.</param>
- void Resolve(IntPtr hwnd, SLR_FLAGS fFlags);
- /// <summary>
- /// Sets the path and file name of a Shell link object
- /// </summary>
- /// <param name="pszFile">The PSZ file.</param>
- void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
-
- }
-
- // CLSID_ShellLink from ShlGuid.h
- /// <summary>
- /// Class ShellLink
- /// </summary>
- [
- ComImport,
- Guid("00021401-0000-0000-C000-000000000046")
- ]
- public class ShellLink
- {
- }
-}
diff --git a/MediaBrowser.ServerApplication/Native/LoopUtil.cs b/MediaBrowser.ServerApplication/Native/LoopUtil.cs
deleted file mode 100644
index 0efdba389..000000000
--- a/MediaBrowser.ServerApplication/Native/LoopUtil.cs
+++ /dev/null
@@ -1,293 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-/*
- * Important - Even though this will compile in the shared projects, it will cause build failures within the mono runtime
- */
-namespace MediaBrowser.ServerApplication.Native
-{
- /// <summary>
- /// http://blogs.msdn.com/b/fiddler/archive/2011/12/10/fiddler-windows-8-apps-enable-LoopUtil-network-isolation-exemption.aspx
- /// </summary>
- public class LoopUtil
- {
- //http://msdn.microsoft.com/en-us/library/windows/desktop/aa379595(v=vs.85).aspx
- [StructLayout(LayoutKind.Sequential)]
- internal struct SID_AND_ATTRIBUTES
- {
- public IntPtr Sid;
- public uint Attributes;
- }
-
- [StructLayoutAttribute(LayoutKind.Sequential)]
- internal struct INET_FIREWALL_AC_CAPABILITIES
- {
- public uint count;
- public IntPtr capabilities; //SID_AND_ATTRIBUTES
- }
-
- [StructLayoutAttribute(LayoutKind.Sequential)]
- internal struct INET_FIREWALL_AC_BINARIES
- {
- public uint count;
- public IntPtr binaries;
- }
-
- [StructLayoutAttribute(LayoutKind.Sequential)]
- internal struct INET_FIREWALL_APP_CONTAINER
- {
- internal IntPtr appContainerSid;
- internal IntPtr userSid;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string appContainerName;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string displayName;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string description;
- internal INET_FIREWALL_AC_CAPABILITIES capabilities;
- internal INET_FIREWALL_AC_BINARIES binaries;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string workingDirectory;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string packageFullName;
- }
-
-
- // Call this API to load the current list of LoopUtil-enabled AppContainers
- [DllImport("FirewallAPI.dll")]
- internal static extern uint NetworkIsolationGetAppContainerConfig(out uint pdwCntACs, out IntPtr appContainerSids);
-
- // Call this API to set the LoopUtil-exemption list
- [DllImport("FirewallAPI.dll")]
- private static extern uint NetworkIsolationSetAppContainerConfig(uint pdwCntACs, SID_AND_ATTRIBUTES[] appContainerSids);
-
- // Use this API to convert a string SID into an actual SID
- [DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool ConvertStringSidToSid(string strSid, out IntPtr pSid);
-
- [DllImport("advapi32", /*CharSet = CharSet.Auto,*/ SetLastError = true)]
- static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);
-
- // Call this API to enumerate all of the AppContainers on the system
- [DllImport("FirewallAPI.dll")]
- internal static extern uint NetworkIsolationEnumAppContainers(uint Flags, out uint pdwCntPublicACs, out IntPtr ppACs);
- // DWORD NetworkIsolationEnumAppContainers(
- // _In_ DWORD Flags,
- // _Out_ DWORD *pdwNumPublicAppCs,
- // _Out_ PINET_FIREWALL_APP_CONTAINER *ppPublicAppCs
- //);
-
- //http://msdn.microsoft.com/en-gb/library/windows/desktop/hh968116.aspx
- enum NETISO_FLAG
- {
- NETISO_FLAG_FORCE_COMPUTE_BINARIES = 0x1,
- NETISO_FLAG_MAX = 0x2
- }
-
-
- public class AppContainer
- {
- public String appContainerName { get; set; }
- public String displayName { get; set; }
- public String workingDirectory { get; set; }
- public String StringSid { get; set; }
- public List<uint> capabilities { get; set; }
- public bool LoopUtil { get; set; }
-
- public AppContainer(String _appContainerName, String _displayName, String _workingDirectory, IntPtr _sid)
- {
- this.appContainerName = _appContainerName;
- this.displayName = _displayName;
- this.workingDirectory = _workingDirectory;
- String tempSid;
- ConvertSidToStringSid(_sid, out tempSid);
- this.StringSid = tempSid;
- }
- }
-
- internal List<LoopUtil.INET_FIREWALL_APP_CONTAINER> _AppList;
- internal List<LoopUtil.SID_AND_ATTRIBUTES> _AppListConfig;
- public List<AppContainer> Apps = new List<AppContainer>();
- internal IntPtr _pACs;
-
- public LoopUtil()
- {
- LoadApps();
- }
-
- public void LoadApps()
- {
- Apps.Clear();
- _pACs = IntPtr.Zero;
- //Full List of Apps
- _AppList = PI_NetworkIsolationEnumAppContainers();
- //List of Apps that have LoopUtil enabled.
- _AppListConfig = PI_NetworkIsolationGetAppContainerConfig();
- foreach (var PI_app in _AppList)
- {
- AppContainer app = new AppContainer(PI_app.appContainerName, PI_app.displayName, PI_app.workingDirectory, PI_app.appContainerSid);
-
- app.LoopUtil = CheckLoopback(PI_app.appContainerSid);
- Apps.Add(app);
- }
- }
- private bool CheckLoopback(IntPtr intPtr)
- {
- foreach (SID_AND_ATTRIBUTES item in _AppListConfig)
- {
- string left, right;
- ConvertSidToStringSid(item.Sid, out left);
- ConvertSidToStringSid(intPtr, out right);
-
- if (left == right)
- {
- return true;
- }
- }
- return false;
- }
-
- private bool CreateExcemptions(string appName)
- {
- var hasChanges = false;
-
- foreach (var app in Apps)
- {
- if ((app.appContainerName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1 ||
- (app.displayName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1)
- {
- if (!app.LoopUtil)
- {
- app.LoopUtil = true;
- hasChanges = true;
- }
- }
- }
-
- return hasChanges;
- }
-
- public static void Run(string appName)
- {
- var util = new LoopUtil();
- util.LoadApps();
-
- var hasChanges = util.CreateExcemptions(appName);
-
- if (hasChanges)
- {
- util.SaveLoopbackState();
- }
- }
-
- private static List<SID_AND_ATTRIBUTES> PI_NetworkIsolationGetAppContainerConfig()
- {
-
- IntPtr arrayValue = IntPtr.Zero;
- uint size = 0;
- var list = new List<SID_AND_ATTRIBUTES>();
-
- // Pin down variables
- GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned);
- GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned);
-
- uint retval = NetworkIsolationGetAppContainerConfig(out size, out arrayValue);
-
- var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES));
- for (var i = 0; i < size; i++)
- {
- var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES));
- list.Add(cur);
- arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
- }
-
- //release pinned variables.
- handle_pdwCntPublicACs.Free();
- handle_ppACs.Free();
-
- return list;
-
-
- }
-
- private List<INET_FIREWALL_APP_CONTAINER> PI_NetworkIsolationEnumAppContainers()
- {
-
- IntPtr arrayValue = IntPtr.Zero;
- uint size = 0;
- var list = new List<INET_FIREWALL_APP_CONTAINER>();
-
- // Pin down variables
- GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned);
- GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned);
-
- //uint retval2 = NetworkIsolationGetAppContainerConfig( out size, out arrayValue);
-
- uint retval = NetworkIsolationEnumAppContainers((Int32)NETISO_FLAG.NETISO_FLAG_MAX, out size, out arrayValue);
- _pACs = arrayValue; //store the pointer so it can be freed when we close the form
-
- var structSize = Marshal.SizeOf(typeof(INET_FIREWALL_APP_CONTAINER));
- for (var i = 0; i < size; i++)
- {
- var cur = (INET_FIREWALL_APP_CONTAINER)Marshal.PtrToStructure(arrayValue, typeof(INET_FIREWALL_APP_CONTAINER));
- list.Add(cur);
- arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
- }
-
- //release pinned variables.
- handle_pdwCntPublicACs.Free();
- handle_ppACs.Free();
-
- return list;
-
-
- }
-
- public bool SaveLoopbackState()
- {
- var countEnabled = CountEnabledLoopUtil();
- SID_AND_ATTRIBUTES[] arr = new SID_AND_ATTRIBUTES[countEnabled];
- int count = 0;
-
- for (int i = 0; i < Apps.Count; i++)
- {
- if (Apps[i].LoopUtil)
- {
- arr[count].Attributes = 0;
- //TO DO:
- IntPtr ptr;
- ConvertStringSidToSid(Apps[i].StringSid, out ptr);
- arr[count].Sid = ptr;
- count++;
- }
-
- }
-
-
- if (NetworkIsolationSetAppContainerConfig((uint)countEnabled, arr) == 0)
- {
- return true;
- }
- else
- { return false; }
-
- }
-
- private int CountEnabledLoopUtil()
- {
- var count = 0;
- for (int i = 0; i < Apps.Count; i++)
- {
- if (Apps[i].LoopUtil)
- {
- count++;
- }
-
- }
- return count;
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Native/PowerManagement.cs b/MediaBrowser.ServerApplication/Native/PowerManagement.cs
deleted file mode 100644
index 0bd3db1da..000000000
--- a/MediaBrowser.ServerApplication/Native/PowerManagement.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- public class PowerManagement : IPowerManagement
- {
- public void PreventSystemStandby()
- {
- MainStartup.Invoke(Standby.PreventSleep);
- }
-
- public void AllowSystemStandby()
- {
- MainStartup.Invoke(Standby.AllowSleep);
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Native/RegisterServer.bat b/MediaBrowser.ServerApplication/Native/RegisterServer.bat
deleted file mode 100644
index 504df2199..000000000
--- a/MediaBrowser.ServerApplication/Native/RegisterServer.bat
+++ /dev/null
@@ -1,34 +0,0 @@
-rem %1 = udp server port
-rem %2 = http server port
-rem %3 = https server port
-rem %4 = exe path
-
-if [%1]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %1" protocol=UDP localport=%1
-netsh advfirewall firewall add rule name="Port %1" dir=in action=allow protocol=UDP localport=%1
-
-if [%2]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %2" protocol=TCP localport=%2
-netsh advfirewall firewall add rule name="Port %2" dir=in action=allow protocol=TCP localport=%2
-
-if [%3]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %3" protocol=TCP localport=%3
-netsh advfirewall firewall add rule name="Port %3" dir=in action=allow protocol=TCP localport=%3
-
-if [%4]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="mediabrowser.serverapplication.exe"
-netsh advfirewall firewall delete rule name="Emby Server"
-
-netsh advfirewall firewall add rule name="Emby Server" dir=in action=allow protocol=TCP program=%4 enable=yes
-netsh advfirewall firewall add rule name="Emby Server" dir=in action=allow protocol=UDP program=%4 enable=yes
-
-netsh advfirewall firewall add rule name="mediabrowser.serverapplication.exe" dir=in action=allow protocol=TCP program=%4 enable=yes
-netsh advfirewall firewall add rule name="mediabrowser.serverapplication.exe" dir=in action=allow protocol=UDP program=%4 enable=yes
-
-
-:DONE
-Exit \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs b/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs
deleted file mode 100644
index 70444ad9f..000000000
--- a/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Reflection;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- /// <summary>
- /// Class Authorization
- /// </summary>
- public static class ServerAuthorization
- {
- /// <summary>
- /// Authorizes the server.
- /// </summary>
- /// <param name="udpPort">The UDP port.</param>
- /// <param name="httpServerPort">The HTTP server port.</param>
- /// <param name="httpsServerPort">The HTTPS server port.</param>
- /// <param name="tempDirectory">The temp directory.</param>
- public static void AuthorizeServer(int udpPort, int httpServerPort, int httpsServerPort, string applicationPath, string tempDirectory)
- {
- Directory.CreateDirectory(tempDirectory);
-
- // Create a temp file path to extract the bat file to
- var tmpFile = Path.Combine(tempDirectory, Guid.NewGuid() + ".bat");
-
- // Extract the bat file
- using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(ServerAuthorization).Namespace + ".RegisterServer.bat"))
- {
- using (var fileStream = File.Create(tmpFile))
- {
- stream.CopyTo(fileStream);
- }
- }
-
- var startInfo = new ProcessStartInfo
- {
- FileName = tmpFile,
-
- Arguments = string.Format("{0} {1} {2} \"{3}\"", udpPort, httpServerPort, httpsServerPort, applicationPath),
-
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas",
- ErrorDialog = false
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.WaitForExit();
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Native/Standby.cs b/MediaBrowser.ServerApplication/Native/Standby.cs
deleted file mode 100644
index 919709538..000000000
--- a/MediaBrowser.ServerApplication/Native/Standby.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- /// <summary>
- /// Class NativeApp
- /// </summary>
- public static class Standby
- {
- public static void PreventSleepAndMonitorOff()
- {
- NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED | NativeMethods.ES_DISPLAY_REQUIRED);
- }
-
- public static void PreventSleep()
- {
- NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED);
- }
-
- // Clear EXECUTION_STATE flags to allow the system to sleep and turn off monitor normally
- public static void AllowSleep()
- {
- NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS);
- }
-
- internal static class NativeMethods
- {
- // Import SetThreadExecutionState Win32 API and necessary flags
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- public static extern uint SetThreadExecutionState(uint esFlags);
- public const uint ES_CONTINUOUS = 0x80000000;
- public const uint ES_SYSTEM_REQUIRED = 0x00000001;
- public const uint ES_DISPLAY_REQUIRED = 0x00000002;
- }
-
- [Flags]
- internal enum EXECUTION_STATE : uint
- {
- ES_NONE = 0,
- ES_SYSTEM_REQUIRED = 0x00000001,
- ES_DISPLAY_REQUIRED = 0x00000002,
- ES_USER_PRESENT = 0x00000004,
- ES_AWAYMODE_REQUIRED = 0x00000040,
- ES_CONTINUOUS = 0x80000000
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Networking/NativeMethods.cs b/MediaBrowser.ServerApplication/Networking/NativeMethods.cs
deleted file mode 100644
index 037b1f75b..000000000
--- a/MediaBrowser.ServerApplication/Networking/NativeMethods.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Security;
-
-namespace MediaBrowser.ServerApplication.Networking
-{
- /// <summary>
- /// Class NativeMethods
- /// </summary>
- [SuppressUnmanagedCodeSecurity]
- public static class NativeMethods
- {
- //declare the Netapi32 : NetServerEnum method import
- /// <summary>
- /// Nets the server enum.
- /// </summary>
- /// <param name="ServerName">Name of the server.</param>
- /// <param name="dwLevel">The dw level.</param>
- /// <param name="pBuf">The p buf.</param>
- /// <param name="dwPrefMaxLen">The dw pref max len.</param>
- /// <param name="dwEntriesRead">The dw entries read.</param>
- /// <param name="dwTotalEntries">The dw total entries.</param>
- /// <param name="dwServerType">Type of the dw server.</param>
- /// <param name="domain">The domain.</param>
- /// <param name="dwResumeHandle">The dw resume handle.</param>
- /// <returns>System.Int32.</returns>
- [DllImport("Netapi32", CharSet = CharSet.Auto, SetLastError = true),
- SuppressUnmanagedCodeSecurity]
-
- public static extern int NetServerEnum(
- string ServerName, // must be null
- int dwLevel,
- ref IntPtr pBuf,
- int dwPrefMaxLen,
- out int dwEntriesRead,
- out int dwTotalEntries,
- int dwServerType,
- string domain, // null for login domain
- out int dwResumeHandle
- );
-
- //declare the Netapi32 : NetApiBufferFree method import
- /// <summary>
- /// Nets the API buffer free.
- /// </summary>
- /// <param name="pBuf">The p buf.</param>
- /// <returns>System.Int32.</returns>
- [DllImport("Netapi32", SetLastError = true),
- SuppressUnmanagedCodeSecurity]
- public static extern int NetApiBufferFree(
- IntPtr pBuf);
-
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool FileTimeToSystemTime(
- [In] ref long fileTime,
- out SystemTime systemTime);
-
- [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CryptAcquireContextW(
- out IntPtr providerContext,
- [MarshalAs(UnmanagedType.LPWStr)] string container,
- [MarshalAs(UnmanagedType.LPWStr)] string provider,
- int providerType,
- int flags);
-
- [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CryptReleaseContext(
- IntPtr providerContext,
- int flags);
-
- [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CryptGenKey(
- IntPtr providerContext,
- int algorithmId,
- int flags,
- out IntPtr cryptKeyHandle);
-
- [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CryptDestroyKey(
- IntPtr cryptKeyHandle);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CertStrToNameW(
- int certificateEncodingType,
- IntPtr x500,
- int strType,
- IntPtr reserved,
- [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] encoded,
- ref int encodedLength,
- out IntPtr errorString);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern IntPtr CertCreateSelfSignCertificate(
- IntPtr providerHandle,
- [In] ref CryptoApiBlob subjectIssuerBlob,
- int flags,
- [In] ref CryptKeyProviderInformation keyProviderInformation,
- [In] ref CryptAlgorithmIdentifier algorithmIdentifier,
- [In] ref SystemTime startTime,
- [In] ref SystemTime endTime,
- IntPtr extensions);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CertFreeCertificateContext(
- IntPtr certificateContext);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern IntPtr CertOpenStore(
- [MarshalAs(UnmanagedType.LPStr)] string storeProvider,
- int messageAndCertificateEncodingType,
- IntPtr cryptProvHandle,
- int flags,
- IntPtr parameters);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CertCloseStore(
- IntPtr certificateStoreHandle,
- int flags);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CertAddCertificateContextToStore(
- IntPtr certificateStoreHandle,
- IntPtr certificateContext,
- int addDisposition,
- out IntPtr storeContextPtr);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CertSetCertificateContextProperty(
- IntPtr certificateContext,
- int propertyId,
- int flags,
- [In] ref CryptKeyProviderInformation data);
-
- [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool PFXExportCertStoreEx(
- IntPtr certificateStoreHandle,
- ref CryptoApiBlob pfxBlob,
- IntPtr password,
- IntPtr reserved,
- int flags);
- }
-
- //create a _SERVER_INFO_100 STRUCTURE
- /// <summary>
- /// Struct _SERVER_INFO_100
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct _SERVER_INFO_100
- {
- /// <summary>
- /// The sv100_platform_id
- /// </summary>
- internal int sv100_platform_id;
- /// <summary>
- /// The sv100_name
- /// </summary>
- [MarshalAs(UnmanagedType.LPWStr)]
- internal string sv100_name;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct SystemTime
- {
- public short Year;
- public short Month;
- public short DayOfWeek;
- public short Day;
- public short Hour;
- public short Minute;
- public short Second;
- public short Milliseconds;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct CryptObjIdBlob
- {
- public uint cbData;
- public IntPtr pbData;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct CryptAlgorithmIdentifier
- {
- [MarshalAs(UnmanagedType.LPStr)]
- public String pszObjId;
- public CryptObjIdBlob Parameters;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct CryptoApiBlob
- {
- public int DataLength;
- public IntPtr Data;
-
- public CryptoApiBlob(int dataLength, IntPtr data)
- {
- this.DataLength = dataLength;
- this.Data = data;
- }
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct CryptKeyProviderInformation
- {
- [MarshalAs(UnmanagedType.LPWStr)]
- public string ContainerName;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string ProviderName;
- public int ProviderType;
- public int Flags;
- public int ProviderParameterCount;
- public IntPtr ProviderParameters; // PCRYPT_KEY_PROV_PARAM
- public int KeySpec;
- }
-}
diff --git a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs b/MediaBrowser.ServerApplication/Networking/NetworkManager.cs
deleted file mode 100644
index 8933a5760..000000000
--- a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace MediaBrowser.ServerApplication.Networking
-{
- /// <summary>
- /// Class NetUtils
- /// </summary>
- public class NetworkManager : Emby.Server.Implementations.Networking.NetworkManager
- {
- public NetworkManager(ILogger logger)
- : base(logger)
- {
- }
-
- /// <summary>
- /// Gets the network shares.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{NetworkShare}.</returns>
- public override IEnumerable<NetworkShare> GetNetworkShares(string path)
- {
- Logger.Info("Getting network shares from {0}", path);
- return new ShareCollection(path).OfType<Share>().Select(ToNetworkShare);
- }
-
- /// <summary>
- /// To the network share.
- /// </summary>
- /// <param name="share">The share.</param>
- /// <returns>NetworkShare.</returns>
- private NetworkShare ToNetworkShare(Share share)
- {
- return new NetworkShare
- {
- Name = share.NetName,
- Path = share.Path,
- Remark = share.Remark,
- Server = share.Server,
- ShareType = ToNetworkShareType(share.ShareType)
- };
- }
-
- /// <summary>
- /// To the type of the network share.
- /// </summary>
- /// <param name="shareType">Type of the share.</param>
- /// <returns>NetworkShareType.</returns>
- /// <exception cref="System.ArgumentException">Unknown share type</exception>
- private NetworkShareType ToNetworkShareType(ShareType shareType)
- {
- if (shareType.HasFlag(ShareType.Special))
- {
- return NetworkShareType.Special;
- }
- if (shareType.HasFlag(ShareType.Device))
- {
- return NetworkShareType.Device;
- }
- if (shareType.HasFlag(ShareType.Disk))
- {
- return NetworkShareType.Disk;
- }
- if (shareType.HasFlag(ShareType.IPC))
- {
- return NetworkShareType.Ipc;
- }
- if (shareType.HasFlag(ShareType.Printer))
- {
- return NetworkShareType.Printer;
- }
- throw new ArgumentException("Unknown share type");
- }
-
- /// <summary>
- /// Uses the DllImport : NetServerEnum with all its required parameters
- /// (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
- /// for full details or method signature) to retrieve a list of domain SV_TYPE_WORKSTATION
- /// and SV_TYPE_SERVER PC's
- /// </summary>
- /// <returns>Arraylist that represents all the SV_TYPE_WORKSTATION and SV_TYPE_SERVER
- /// PC's in the Domain</returns>
- private List<string> GetNetworkDevicesInternal()
- {
- //local fields
- const int MAX_PREFERRED_LENGTH = -1;
- var SV_TYPE_WORKSTATION = 1;
- var SV_TYPE_SERVER = 2;
- IntPtr buffer = IntPtr.Zero;
- IntPtr tmpBuffer = IntPtr.Zero;
- var entriesRead = 0;
- var totalEntries = 0;
- var resHandle = 0;
- var sizeofINFO = Marshal.SizeOf(typeof(_SERVER_INFO_100));
-
- var returnList = new List<string>();
-
- try
- {
- //call the DllImport : NetServerEnum with all its required parameters
- //see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
- //for full details of method signature
- var ret = NativeMethods.NetServerEnum(null, 100, ref buffer, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, SV_TYPE_WORKSTATION | SV_TYPE_SERVER, null, out resHandle);
-
- //if the returned with a NERR_Success (C++ term), =0 for C#
- if (ret == 0)
- {
- //loop through all SV_TYPE_WORKSTATION and SV_TYPE_SERVER PC's
- for (var i = 0; i < totalEntries; i++)
- {
- //get pointer to, Pointer to the buffer that received the data from
- //the call to NetServerEnum. Must ensure to use correct size of
- //STRUCTURE to ensure correct location in memory is pointed to
- tmpBuffer = new IntPtr((Int64)buffer + (i * sizeofINFO));
- //Have now got a pointer to the list of SV_TYPE_WORKSTATION and
- //SV_TYPE_SERVER PC's, which is unmanaged memory
- //Needs to Marshal data from an unmanaged block of memory to a
- //managed object, again using STRUCTURE to ensure the correct data
- //is marshalled
- var svrInfo = (_SERVER_INFO_100)Marshal.PtrToStructure(tmpBuffer, typeof(_SERVER_INFO_100));
-
- //add the PC names to the ArrayList
- if (!string.IsNullOrEmpty(svrInfo.sv100_name))
- {
- returnList.Add(svrInfo.sv100_name);
- }
- }
- }
- }
- finally
- {
- //The NetApiBufferFree function frees
- //the memory that the NetApiBufferAllocate function allocates
- NativeMethods.NetApiBufferFree(buffer);
- }
-
- return returnList;
- }
-
- /// <summary>
- /// Gets available devices within the domain
- /// </summary>
- /// <returns>PC's in the Domain</returns>
- public override IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
- {
- return GetNetworkDevicesInternal().Select(c => new FileSystemEntryInfo
- {
- Name = c,
- Path = NetworkPrefix + c,
- Type = FileSystemEntryType.NetworkComputer
- });
- }
-
- /// <summary>
- /// Gets the network prefix.
- /// </summary>
- /// <value>The network prefix.</value>
- private string NetworkPrefix
- {
- get
- {
- var separator = Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture);
- return separator + separator;
- }
- }
- }
-
-}
diff --git a/MediaBrowser.ServerApplication/Networking/NetworkShares.cs b/MediaBrowser.ServerApplication/Networking/NetworkShares.cs
deleted file mode 100644
index f9a59203d..000000000
--- a/MediaBrowser.ServerApplication/Networking/NetworkShares.cs
+++ /dev/null
@@ -1,643 +0,0 @@
-using System;
-using System.IO;
-using System.Collections;
-using System.Runtime.InteropServices;
-
-namespace MediaBrowser.ServerApplication.Networking
-{
- /// <summary>
- /// Type of share
- /// </summary>
- [Flags]
- public enum ShareType
- {
- /// <summary>Disk share</summary>
- Disk = 0,
- /// <summary>Printer share</summary>
- Printer = 1,
- /// <summary>Device share</summary>
- Device = 2,
- /// <summary>IPC share</summary>
- IPC = 3,
- /// <summary>Special share</summary>
- Special = -2147483648, // 0x80000000,
- }
-
- #region Share
-
- /// <summary>
- /// Information about a local share
- /// </summary>
- public class Share
- {
- #region Private data
-
- private string _server;
- private string _netName;
- private string _path;
- private ShareType _shareType;
- private string _remark;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="server">The server.</param>
- /// <param name="netName">Name of the net.</param>
- /// <param name="path">The path.</param>
- /// <param name="shareType">Type of the share.</param>
- /// <param name="remark">The remark.</param>
- public Share(string server, string netName, string path, ShareType shareType, string remark)
- {
- if (ShareType.Special == shareType && "IPC$" == netName)
- {
- shareType |= ShareType.IPC;
- }
-
- _server = server;
- _netName = netName;
- _path = path;
- _shareType = shareType;
- _remark = remark;
- }
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// The name of the computer that this share belongs to
- /// </summary>
- public string Server
- {
- get { return _server; }
- }
-
- /// <summary>
- /// Share name
- /// </summary>
- public string NetName
- {
- get { return _netName; }
- }
-
- /// <summary>
- /// Local path
- /// </summary>
- public string Path
- {
- get { return _path; }
- }
-
- /// <summary>
- /// Share type
- /// </summary>
- public ShareType ShareType
- {
- get { return _shareType; }
- }
-
- /// <summary>
- /// Comment
- /// </summary>
- public string Remark
- {
- get { return _remark; }
- }
-
- /// <summary>
- /// Returns true if this is a file system share
- /// </summary>
- public bool IsFileSystem
- {
- get
- {
- // Shared device
- if (0 != (_shareType & ShareType.Device)) return false;
- // IPC share
- if (0 != (_shareType & ShareType.IPC)) return false;
- // Shared printer
- if (0 != (_shareType & ShareType.Printer)) return false;
-
- // Standard disk share
- if (0 == (_shareType & ShareType.Special)) return true;
-
- // Special disk share (e.g. C$)
- return ShareType.Special == _shareType && !string.IsNullOrEmpty(_netName);
- }
- }
-
- /// <summary>
- /// Get the root of a disk-based share
- /// </summary>
- public DirectoryInfo Root
- {
- get
- {
- if (IsFileSystem)
- {
- if (string.IsNullOrEmpty(_server))
- if (string.IsNullOrEmpty(_path))
- return new DirectoryInfo(ToString());
- else
- return new DirectoryInfo(_path);
- return new DirectoryInfo(ToString());
- }
- return null;
- }
- }
-
- #endregion
-
- /// <summary>
- /// Returns the path to this share
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- if (string.IsNullOrEmpty(_server))
- {
- return string.Format(@"\\{0}\{1}", Environment.MachineName, _netName);
- }
- return string.Format(@"\\{0}\{1}", _server, _netName);
- }
-
- /// <summary>
- /// Returns true if this share matches the local path
- /// </summary>
- /// <param name="path"></param>
- /// <returns></returns>
- public bool MatchesPath(string path)
- {
- if (!IsFileSystem) return false;
- if (string.IsNullOrEmpty(path)) return true;
-
- return path.ToLower().StartsWith(_path.ToLower());
- }
- }
-
- #endregion
-
- /// <summary>
- /// A collection of shares
- /// </summary>
- public class ShareCollection : ReadOnlyCollectionBase
- {
- #region Platform
-
- /// <summary>
- /// Is this an NT platform?
- /// </summary>
- protected static bool IsNT
- {
- get { return (PlatformID.Win32NT == Environment.OSVersion.Platform); }
- }
-
- /// <summary>
- /// Returns true if this is Windows 2000 or higher
- /// </summary>
- protected static bool IsW2KUp
- {
- get
- {
- OperatingSystem os = Environment.OSVersion;
- if (PlatformID.Win32NT == os.Platform && os.Version.Major >= 5)
- return true;
- else
- return false;
- }
- }
-
- #endregion
-
- #region Interop
-
- #region Constants
-
- /// <summary>Maximum path length</summary>
- protected const int MAX_PATH = 260;
- /// <summary>No error</summary>
- protected const int NO_ERROR = 0;
- /// <summary>Access denied</summary>
- protected const int ERROR_ACCESS_DENIED = 5;
- /// <summary>Access denied</summary>
- protected const int ERROR_WRONG_LEVEL = 124;
- /// <summary>More data available</summary>
- protected const int ERROR_MORE_DATA = 234;
- /// <summary>Not connected</summary>
- protected const int ERROR_NOT_CONNECTED = 2250;
- /// <summary>Level 1</summary>
- protected const int UNIVERSAL_NAME_INFO_LEVEL = 1;
- /// <summary>Max extries (9x)</summary>
- protected const int MAX_SI50_ENTRIES = 20;
-
- #endregion
-
- #region Structures
-
- /// <summary>Unc name</summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- protected struct UNIVERSAL_NAME_INFO
- {
- [MarshalAs(UnmanagedType.LPTStr)]
- public string lpUniversalName;
- }
-
- /// <summary>Share information, NT, level 2</summary>
- /// <remarks>
- /// Requires admin rights to work.
- /// </remarks>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- protected struct SHARE_INFO_2
- {
- [MarshalAs(UnmanagedType.LPWStr)]
- public string NetName;
- public ShareType ShareType;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string Remark;
- public int Permissions;
- public int MaxUsers;
- public int CurrentUsers;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string Path;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string Password;
- }
-
- /// <summary>Share information, NT, level 1</summary>
- /// <remarks>
- /// Fallback when no admin rights.
- /// </remarks>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- protected struct SHARE_INFO_1
- {
- [MarshalAs(UnmanagedType.LPWStr)]
- public string NetName;
- public ShareType ShareType;
- [MarshalAs(UnmanagedType.LPWStr)]
- public string Remark;
- }
-
- /// <summary>Share information, Win9x</summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
- protected struct SHARE_INFO_50
- {
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
- public string NetName;
-
- public byte bShareType;
- public ushort Flags;
-
- [MarshalAs(UnmanagedType.LPTStr)]
- public string Remark;
- [MarshalAs(UnmanagedType.LPTStr)]
- public string Path;
-
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
- public string PasswordRW;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
- public string PasswordRO;
-
- public ShareType ShareType
- {
- get { return (ShareType)((int)bShareType & 0x7F); }
- }
- }
-
- /// <summary>Share information level 1, Win9x</summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
- protected struct SHARE_INFO_1_9x
- {
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
- public string NetName;
- public byte Padding;
-
- public ushort bShareType;
-
- [MarshalAs(UnmanagedType.LPTStr)]
- public string Remark;
-
- public ShareType ShareType
- {
- get { return (ShareType)((int)bShareType & 0x7FFF); }
- }
- }
-
- #endregion
-
- #region Functions
-
- /// <summary>Get a UNC name</summary>
- [DllImport("mpr", CharSet = CharSet.Auto)]
- protected static extern int WNetGetUniversalName(string lpLocalPath,
- int dwInfoLevel, ref UNIVERSAL_NAME_INFO lpBuffer, ref int lpBufferSize);
-
- /// <summary>Get a UNC name</summary>
- [DllImport("mpr", CharSet = CharSet.Auto)]
- protected static extern int WNetGetUniversalName(string lpLocalPath,
- int dwInfoLevel, IntPtr lpBuffer, ref int lpBufferSize);
-
- /// <summary>Enumerate shares (NT)</summary>
- [DllImport("netapi32", CharSet = CharSet.Unicode)]
- protected static extern int NetShareEnum(string lpServerName, int dwLevel,
- out IntPtr lpBuffer, int dwPrefMaxLen, out int entriesRead,
- out int totalEntries, ref int hResume);
-
- /// <summary>Enumerate shares (9x)</summary>
- [DllImport("svrapi", CharSet = CharSet.Ansi)]
- protected static extern int NetShareEnum(
- [MarshalAs(UnmanagedType.LPTStr)] string lpServerName, int dwLevel,
- IntPtr lpBuffer, ushort cbBuffer, out ushort entriesRead,
- out ushort totalEntries);
-
- /// <summary>Free the buffer (NT)</summary>
- [DllImport("netapi32")]
- protected static extern int NetApiBufferFree(IntPtr lpBuffer);
-
- #endregion
-
- #region Enumerate shares
-
- /// <summary>
- /// Enumerates the shares on Windows NT
- /// </summary>
- /// <param name="server">The server name</param>
- /// <param name="shares">The ShareCollection</param>
- protected static void EnumerateSharesNT(string server, ShareCollection shares)
- {
- int level = 2;
- int entriesRead, totalEntries, nRet, hResume = 0;
- IntPtr pBuffer = IntPtr.Zero;
-
- try
- {
- nRet = NetShareEnum(server, level, out pBuffer, -1,
- out entriesRead, out totalEntries, ref hResume);
-
- if (ERROR_ACCESS_DENIED == nRet)
- {
- //Need admin for level 2, drop to level 1
- level = 1;
- nRet = NetShareEnum(server, level, out pBuffer, -1,
- out entriesRead, out totalEntries, ref hResume);
- }
-
- if (NO_ERROR == nRet && entriesRead > 0)
- {
- Type t = (2 == level) ? typeof(SHARE_INFO_2) : typeof(SHARE_INFO_1);
- int offset = Marshal.SizeOf(t);
-
- var lpItem = pBuffer.ToInt64();
-
- for (int i = 0; i < entriesRead; i++, lpItem += offset)
- {
- IntPtr pItem = new IntPtr(lpItem);
- if (1 == level)
- {
- SHARE_INFO_1 si = (SHARE_INFO_1)Marshal.PtrToStructure(pItem, t);
- shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
- }
- else
- {
- SHARE_INFO_2 si = (SHARE_INFO_2)Marshal.PtrToStructure(pItem, t);
- shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
- }
- }
- }
-
- }
- finally
- {
- // Clean up buffer allocated by system
- if (IntPtr.Zero != pBuffer)
- NetApiBufferFree(pBuffer);
- }
- }
-
- /// <summary>
- /// Enumerates the shares on Windows 9x
- /// </summary>
- /// <param name="server">The server name</param>
- /// <param name="shares">The ShareCollection</param>
- protected static void EnumerateShares9x(string server, ShareCollection shares)
- {
- int level = 50;
- int nRet = 0;
- ushort entriesRead, totalEntries;
-
- var t = typeof(SHARE_INFO_50);
- var size = Marshal.SizeOf(t);
- var cbBuffer = (ushort)(MAX_SI50_ENTRIES * size);
- //On Win9x, must allocate buffer before calling API
- IntPtr pBuffer = Marshal.AllocHGlobal(cbBuffer);
-
- try
- {
- nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
- out entriesRead, out totalEntries);
-
- if (ERROR_WRONG_LEVEL == nRet)
- {
- level = 1;
- t = typeof(SHARE_INFO_1_9x);
- size = Marshal.SizeOf(t);
-
- nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
- out entriesRead, out totalEntries);
- }
-
- if (NO_ERROR == nRet || ERROR_MORE_DATA == nRet)
- {
- for (int i = 0, lpItem = pBuffer.ToInt32(); i < entriesRead; i++, lpItem += size)
- {
- var pItem = new IntPtr(lpItem);
-
- if (1 == level)
- {
- var si = (SHARE_INFO_1_9x)Marshal.PtrToStructure(pItem, t);
- shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
- }
- else
- {
- var si = (SHARE_INFO_50)Marshal.PtrToStructure(pItem, t);
- shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
- }
- }
- }
- else
- Console.WriteLine(nRet);
-
- }
- finally
- {
- //Clean up buffer
- Marshal.FreeHGlobal(pBuffer);
- }
- }
-
- /// <summary>
- /// Enumerates the shares
- /// </summary>
- /// <param name="server">The server name</param>
- /// <param name="shares">The ShareCollection</param>
- protected static void EnumerateShares(string server, ShareCollection shares)
- {
- if (null != server && 0 != server.Length && !IsW2KUp)
- {
- server = server.ToUpper();
-
- // On NT4, 9x and Me, server has to start with "\\"
- if (!('\\' == server[0] && '\\' == server[1]))
- server = @"\\" + server;
- }
-
- if (IsNT)
- EnumerateSharesNT(server, shares);
- else
- EnumerateShares9x(server, shares);
- }
-
- #endregion
-
- #endregion
-
- #region Static methods
-
- /// <summary>
- /// Returns true if fileName is a valid local file-name of the form:
- /// X:\, where X is a drive letter from A-Z
- /// </summary>
- /// <param name="fileName">The filename to check</param>
- /// <returns></returns>
- public static bool IsValidFilePath(string fileName)
- {
- if (null == fileName || 0 == fileName.Length) return false;
-
- char drive = char.ToUpper(fileName[0]);
- if ('A' > drive || drive > 'Z')
- return false;
-
- else if (Path.VolumeSeparatorChar != fileName[1])
- return false;
- else if (Path.DirectorySeparatorChar != fileName[2])
- return false;
- else
- return true;
- }
-
- #endregion
-
- /// <summary>The name of the server this collection represents</summary>
- private string _server;
-
- #region Constructor
-
- /// <summary>
- /// Default constructor - local machine
- /// </summary>
- public ShareCollection()
- {
- _server = string.Empty;
- EnumerateShares(_server, this);
- }
-
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="server">The server.</param>
- public ShareCollection(string server)
- {
- _server = server;
- EnumerateShares(_server, this);
- }
-
- #endregion
-
- #region Add
-
- protected void Add(Share share)
- {
- InnerList.Add(share);
- }
-
- protected void Add(string netName, string path, ShareType shareType, string remark)
- {
- InnerList.Add(new Share(_server, netName, path, shareType, remark));
- }
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Returns the name of the server this collection represents
- /// </summary>
- public string Server
- {
- get { return _server; }
- }
-
- /// <summary>
- /// Returns the <see cref="Share"/> at the specified index.
- /// </summary>
- public Share this[int index]
- {
- get { return (Share)InnerList[index]; }
- }
-
- /// <summary>
- /// Returns the <see cref="Share"/> which matches a given local path
- /// </summary>
- /// <param name="path">The path to match</param>
- public Share this[string path]
- {
- get
- {
- if (null == path || 0 == path.Length) return null;
-
- path = Path.GetFullPath(path);
- if (!IsValidFilePath(path)) return null;
-
- Share match = null;
-
- foreach (object t in InnerList)
- {
- var s = (Share)t;
-
- if (s.IsFileSystem && s.MatchesPath(path))
- {
- //Store first match
- if (null == match)
- match = s;
-
- // If this has a longer path,
- // and this is a disk share or match is a special share,
- // then this is a better match
- else if (match.Path.Length < s.Path.Length)
- {
- if (ShareType.Disk == s.ShareType || ShareType.Disk != match.ShareType)
- match = s;
- }
- }
- }
-
- return match;
- }
- }
-
- #endregion
-
- /// <summary>
- /// Copy this collection to an array
- /// </summary>
- /// <param name="array"></param>
- /// <param name="index"></param>
- public void CopyTo(Share[] array, int index)
- {
- InnerList.CopyTo(array, index);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Properties/Resources.Designer.cs b/MediaBrowser.ServerApplication/Properties/Resources.Designer.cs
deleted file mode 100644
index e166f35d0..000000000
--- a/MediaBrowser.ServerApplication/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace MediaBrowser.ServerApplication.Properties {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MediaBrowser.ServerApplication.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Properties/Resources.resx b/MediaBrowser.ServerApplication/Properties/Resources.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/MediaBrowser.ServerApplication/Properties/Resources.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
-</root> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png b/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png
deleted file mode 100644
index 20c3b9c83..000000000
--- a/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs
deleted file mode 100644
index a8c36e4e8..000000000
--- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.Logging;
-using System;
-using System.ComponentModel;
-using System.Windows.Forms;
-using Emby.Server.Implementations.Browser;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.ServerApplication
-{
- public class ServerNotifyIcon : IDisposable
- {
- private NotifyIcon notifyIcon1;
- private ContextMenuStrip contextMenuStrip1;
- private ToolStripMenuItem cmdExit;
- private ToolStripMenuItem cmdBrowse;
- private ToolStripMenuItem cmdConfigure;
- private ToolStripSeparator toolStripSeparator2;
- private ToolStripMenuItem cmdRestart;
- private ToolStripSeparator toolStripSeparator1;
- private ToolStripMenuItem cmdCommunity;
- private ToolStripMenuItem cmdPremiere;
- private Container components;
-
- private readonly ILogger _logger;
- private readonly IServerApplicationHost _appHost;
- private readonly IServerConfigurationManager _configurationManager;
- private readonly ILocalizationManager _localization;
-
- public void Invoke(Action action)
- {
- contextMenuStrip1.Invoke(action);
- }
-
- public ServerNotifyIcon(ILogManager logManager,
- IServerApplicationHost appHost,
- IServerConfigurationManager configurationManager,
- ILocalizationManager localization)
- {
- _logger = logManager.GetLogger("MainWindow");
- _localization = localization;
- _appHost = appHost;
- _configurationManager = configurationManager;
-
- components = new System.ComponentModel.Container();
-
- contextMenuStrip1 = new ContextMenuStrip(components);
- notifyIcon1 = new NotifyIcon(components);
-
- cmdExit = new ToolStripMenuItem();
- cmdCommunity = new ToolStripMenuItem();
- cmdPremiere = new ToolStripMenuItem();
- toolStripSeparator1 = new ToolStripSeparator();
- cmdRestart = new ToolStripMenuItem();
- toolStripSeparator2 = new ToolStripSeparator();
- cmdConfigure = new ToolStripMenuItem();
- cmdBrowse = new ToolStripMenuItem();
-
- //
- // notifyIcon1
- //
- notifyIcon1.ContextMenuStrip = contextMenuStrip1;
- notifyIcon1.Icon = new System.Drawing.Icon(GetType().Assembly.GetManifestResourceStream(GetType().Namespace + ".Icon.ico"));
- notifyIcon1.Text = "Emby";
- notifyIcon1.Visible = true;
- //
- // contextMenuStrip1
- //
- contextMenuStrip1.Items.AddRange(new ToolStripItem[] {
- cmdBrowse,
- cmdConfigure,
- cmdPremiere,
- toolStripSeparator2,
- cmdRestart,
- toolStripSeparator1,
- cmdCommunity,
- cmdExit});
- contextMenuStrip1.Name = "contextMenuStrip1";
- contextMenuStrip1.ShowCheckMargin = true;
- contextMenuStrip1.ShowImageMargin = false;
- contextMenuStrip1.Size = new System.Drawing.Size(209, 214);
- //
- // cmdExit
- //
- cmdExit.Name = "cmdExit";
- cmdExit.Size = new System.Drawing.Size(208, 22);
- //
- // cmdCommunity
- //
- cmdCommunity.Name = "cmdCommunity";
- cmdCommunity.Size = new System.Drawing.Size(208, 22);
- //
- // cmdPremiere
- //
- cmdPremiere.Name = "cmdPremiere";
- cmdPremiere.Size = new System.Drawing.Size(208, 22);
- //
- // toolStripSeparator1
- //
- toolStripSeparator1.Name = "toolStripSeparator1";
- toolStripSeparator1.Size = new System.Drawing.Size(205, 6);
- //
- // cmdRestart
- //
- cmdRestart.Name = "cmdRestart";
- cmdRestart.Size = new System.Drawing.Size(208, 22);
- //
- // toolStripSeparator2
- //
- toolStripSeparator2.Name = "toolStripSeparator2";
- toolStripSeparator2.Size = new System.Drawing.Size(205, 6);
- //
- // cmdConfigure
- //
- cmdConfigure.Name = "cmdConfigure";
- cmdConfigure.Size = new System.Drawing.Size(208, 22);
- //
- // cmdBrowse
- //
- cmdBrowse.Name = "cmdBrowse";
- cmdBrowse.Size = new System.Drawing.Size(208, 22);
-
- cmdExit.Click += cmdExit_Click;
- cmdRestart.Click += cmdRestart_Click;
- cmdConfigure.Click += cmdConfigure_Click;
- cmdCommunity.Click += cmdCommunity_Click;
- cmdPremiere.Click += cmdPremiere_Click;
- cmdBrowse.Click += cmdBrowse_Click;
-
- _configurationManager.ConfigurationUpdated += Instance_ConfigurationUpdated;
-
- LocalizeText();
-
- notifyIcon1.DoubleClick += notifyIcon1_DoubleClick;
- }
-
- void notifyIcon1_DoubleClick(object sender, EventArgs e)
- {
- BrowserLauncher.OpenDashboard(_appHost);
- }
-
- private void LocalizeText()
- {
- _uiCulture = _configurationManager.Configuration.UICulture;
-
- cmdExit.Text = "Exit";
- cmdCommunity.Text = "Visit Emby Community";
- cmdPremiere.Text = "Emby Premiere";
- cmdBrowse.Text = "Browse Library";
- cmdConfigure.Text = "Configure Emby Server";
- cmdRestart.Text = "Restart Emby Server";
- }
-
- private string _uiCulture;
- /// <summary>
- /// Handles the ConfigurationUpdated event of the Instance control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- void Instance_ConfigurationUpdated(object sender, EventArgs e)
- {
- if (!string.Equals(_configurationManager.Configuration.UICulture, _uiCulture,
- StringComparison.OrdinalIgnoreCase))
- {
- LocalizeText();
- }
- }
-
- void cmdBrowse_Click(object sender, EventArgs e)
- {
- BrowserLauncher.OpenWebClient(_appHost);
- }
-
- void cmdPremiere_Click(object sender, EventArgs e)
- {
- BrowserLauncher.OpenEmbyPremiere(_appHost);
- }
-
- void cmdCommunity_Click(object sender, EventArgs e)
- {
- BrowserLauncher.OpenCommunity(_appHost);
- }
-
- void cmdConfigure_Click(object sender, EventArgs e)
- {
- BrowserLauncher.OpenDashboard(_appHost);
- }
-
- void cmdRestart_Click(object sender, EventArgs e)
- {
- _appHost.Restart();
- }
-
- void cmdExit_Click(object sender, EventArgs e)
- {
- _appHost.Shutdown();
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (notifyIcon1 != null)
- {
- notifyIcon1.Visible = false;
- notifyIcon1.Dispose();
- notifyIcon1 = null;
- }
-
- if (components != null)
- {
- components.Dispose();
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Splash/SplashForm.Designer.cs b/MediaBrowser.ServerApplication/Splash/SplashForm.Designer.cs
deleted file mode 100644
index 9e6086dc2..000000000
--- a/MediaBrowser.ServerApplication/Splash/SplashForm.Designer.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-namespace MediaBrowser.ServerApplication.Splash
-{
- partial class SplashForm
- {
- /// <summary>
- /// Required designer variable.
- /// </summary>
- private System.ComponentModel.IContainer components = null;
-
- /// <summary>
- /// Clean up any resources being used.
- /// </summary>
- /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- /// <summary>
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- /// </summary>
- private void InitializeComponent()
- {
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SplashForm));
- this.panelMainContainer = new System.Windows.Forms.Panel();
- this.panel3 = new System.Windows.Forms.Panel();
- this.panel2 = new System.Windows.Forms.Panel();
- this.panelProgress = new System.Windows.Forms.Panel();
- this.panel4 = new System.Windows.Forms.Panel();
- this.lblVersion = new System.Windows.Forms.Label();
- this.lblStatus = new System.Windows.Forms.Label();
- this.panel1 = new System.Windows.Forms.Panel();
- this.pictureBox1 = new System.Windows.Forms.PictureBox();
- this.panelMainContainer.SuspendLayout();
- this.panel2.SuspendLayout();
- this.panel1.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
- this.SuspendLayout();
- //
- // panelMainContainer
- //
- this.panelMainContainer.BackColor = System.Drawing.Color.Gray;
- this.panelMainContainer.Controls.Add(this.panel3);
- this.panelMainContainer.Controls.Add(this.panel2);
- this.panelMainContainer.Controls.Add(this.panel1);
- this.panelMainContainer.Dock = System.Windows.Forms.DockStyle.Fill;
- this.panelMainContainer.Location = new System.Drawing.Point(0, 0);
- this.panelMainContainer.Name = "panelMainContainer";
- this.panelMainContainer.Padding = new System.Windows.Forms.Padding(1);
- this.panelMainContainer.Size = new System.Drawing.Size(648, 347);
- this.panelMainContainer.TabIndex = 0;
- this.panelMainContainer.UseWaitCursor = true;
- //
- // panel3
- //
- this.panel3.BackColor = System.Drawing.Color.White;
- this.panel3.Dock = System.Windows.Forms.DockStyle.Bottom;
- this.panel3.Location = new System.Drawing.Point(1, 277);
- this.panel3.Name = "panel3";
- this.panel3.Size = new System.Drawing.Size(646, 69);
- this.panel3.TabIndex = 2;
- this.panel3.UseWaitCursor = true;
- //
- // panel2
- //
- this.panel2.BackColor = System.Drawing.Color.Gray;
- this.panel2.Controls.Add(this.panelProgress);
- this.panel2.Controls.Add(this.panel4);
- this.panel2.Controls.Add(this.lblVersion);
- this.panel2.Controls.Add(this.lblStatus);
- this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
- this.panel2.Location = new System.Drawing.Point(1, 141);
- this.panel2.Name = "panel2";
- this.panel2.Size = new System.Drawing.Size(646, 205);
- this.panel2.TabIndex = 1;
- this.panel2.UseWaitCursor = true;
- //
- // panelProgress
- //
- this.panelProgress.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(82)))), ((int)(((byte)(181)))), ((int)(((byte)(75)))));
- this.panelProgress.Location = new System.Drawing.Point(0, 125);
- this.panelProgress.Name = "panelProgress";
- this.panelProgress.Size = new System.Drawing.Size(0, 13);
- this.panelProgress.TabIndex = 3;
- this.panelProgress.UseWaitCursor = true;
- //
- // panel4
- //
- this.panel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(73)))), ((int)(((byte)(73)))), ((int)(((byte)(73)))));
- this.panel4.Location = new System.Drawing.Point(0, 125);
- this.panel4.Name = "panel4";
- this.panel4.Size = new System.Drawing.Size(648, 13);
- this.panel4.TabIndex = 2;
- this.panel4.UseWaitCursor = true;
- //
- // lblVersion
- //
- this.lblVersion.AutoSize = true;
- this.lblVersion.Font = new System.Drawing.Font("Segoe UI", 32F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.lblVersion.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(215)))), ((int)(((byte)(215)))));
- this.lblVersion.Location = new System.Drawing.Point(3, 59);
- this.lblVersion.MaximumSize = new System.Drawing.Size(0, 100);
- this.lblVersion.Name = "lblVersion";
- this.lblVersion.Size = new System.Drawing.Size(267, 59);
- this.lblVersion.TabIndex = 1;
- this.lblVersion.Text = "Version 1234";
- this.lblVersion.UseWaitCursor = true;
- //
- // lblStatus
- //
- this.lblStatus.AutoSize = true;
- this.lblStatus.Font = new System.Drawing.Font("Segoe UI", 32F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.lblStatus.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(215)))), ((int)(((byte)(215)))));
- this.lblStatus.Location = new System.Drawing.Point(3, 0);
- this.lblStatus.MaximumSize = new System.Drawing.Size(0, 100);
- this.lblStatus.Name = "lblStatus";
- this.lblStatus.Size = new System.Drawing.Size(423, 59);
- this.lblStatus.TabIndex = 0;
- this.lblStatus.Text = "Loading Emby Server";
- this.lblStatus.UseWaitCursor = true;
- //
- // panel1
- //
- this.panel1.BackColor = System.Drawing.Color.White;
- this.panel1.Controls.Add(this.pictureBox1);
- this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
- this.panel1.Location = new System.Drawing.Point(1, 1);
- this.panel1.Name = "panel1";
- this.panel1.Size = new System.Drawing.Size(646, 140);
- this.panel1.TabIndex = 0;
- this.panel1.UseWaitCursor = true;
- //
- // pictureBox1
- //
- this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
- this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
- this.pictureBox1.Location = new System.Drawing.Point(0, 0);
- this.pictureBox1.Name = "pictureBox1";
- this.pictureBox1.Size = new System.Drawing.Size(646, 140);
- this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
- this.pictureBox1.TabIndex = 0;
- this.pictureBox1.TabStop = false;
- this.pictureBox1.UseWaitCursor = true;
- //
- // SplashForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(648, 347);
- this.ControlBox = false;
- this.Controls.Add(this.panelMainContainer);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.MaximizeBox = false;
- this.Name = "SplashForm";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
- this.Text = "Emby";
- this.UseWaitCursor = true;
- this.panelMainContainer.ResumeLayout(false);
- this.panel2.ResumeLayout(false);
- this.panel2.PerformLayout();
- this.panel1.ResumeLayout(false);
- ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
- this.ResumeLayout(false);
-
- }
-
- #endregion
-
- private System.Windows.Forms.Panel panelMainContainer;
- private System.Windows.Forms.Panel panel1;
- private System.Windows.Forms.Panel panel2;
- private System.Windows.Forms.Panel panel3;
- private System.Windows.Forms.Label lblStatus;
- private System.Windows.Forms.Label lblVersion;
- private System.Windows.Forms.Panel panel4;
- private System.Windows.Forms.Panel panelProgress;
- private System.Windows.Forms.PictureBox pictureBox1;
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Splash/SplashForm.cs b/MediaBrowser.ServerApplication/Splash/SplashForm.cs
deleted file mode 100644
index 1260c3405..000000000
--- a/MediaBrowser.ServerApplication/Splash/SplashForm.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace MediaBrowser.ServerApplication.Splash
-{
- public partial class SplashForm : Form
- {
- private readonly TaskScheduler _uiThread;
-
- private readonly Progress<double> _progress;
-
- public SplashForm(Version version, Progress<double> progress)
- {
- InitializeComponent();
-
- lblVersion.Text = string.Format("Version {0}...", version);
-
- _progress = progress;
-
- progress.ProgressChanged += progress_ProgressChanged;
- _uiThread = TaskScheduler.FromCurrentSynchronizationContext();
- }
-
- async void progress_ProgressChanged(object sender, double e)
- {
- await Task.Factory.StartNew(() =>
- {
- var width = e * 6.48;
-
- panelProgress.Width = Convert.ToInt32(width);
-
- }, CancellationToken.None, TaskCreationOptions.None, _uiThread);
- }
-
- protected override void OnClosing(CancelEventArgs e)
- {
- _progress.ProgressChanged += progress_ProgressChanged;
-
- base.OnClosing(e);
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Splash/SplashForm.resx b/MediaBrowser.ServerApplication/Splash/SplashForm.resx
deleted file mode 100644
index 99fcc8b74..000000000
--- a/MediaBrowser.ServerApplication/Splash/SplashForm.resx
+++ /dev/null
@@ -1,666 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>
- iVBORw0KGgoAAAANSUhEUgAAAoYAAACMCAIAAABu7P9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wwAADsMBx2+oZAAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAFNhJREFUeF7t3YtX
- FPXfwPHnr3sSRVQ0vISm6TH9WWrZQ5qWZoapiZqZFyztmIBgZN79IRko3lBDEMkVxRSkQkRFvIQszwf2
- A+3O7MzO7s7CsLxf53s6sfuZ3QXP2ffOXmb/pxsAAHgASQYAwBNIMgAAnkCSAQDwBJIMAIAnkGQAADyB
- JAMA4AkkGQAATyDJAAB4AkkGAMATSDIAAJ5AkgEA8ASSDACAJ5BkAAA8gSQDAOAJJBkAAE8gyQAAeAJJ
- BgDAE0gyAACeQJIBAPAEkgwAgCeQZAAAPIEkAwDgCSQZAABPIMkAAHgCSQYAwBNIMgAAnkCSAQDwBJIM
- AIAnkGQAADyBJAMA4AkkOeG6uru2Vm8+3PCz/gwAQDgkObG6/F3fXP0q/cio8UdSD98+oKcCAGBCkhNI
- erzl6ibpsa7Dow5RZQCABZKcKL37x0E97qvy4ds8gw0ACIMkJ4Rx/zh4sa8MAAiHJLvPrseBRZUBACYk
- 2WXS46+rNhobHG4dvPWTbgMAAEl2V0+PrzrqcWAdvE2VAQCKJLsm2h4HFs9ge9CePXveC2fv3r06AQAJ
- QJLdIT3eXLXBkFtHi9eVveeLL77433DWrl2rEwCQACTZBb2vH8fU48Ciyh5DkgEMCpIcr3h7HFhU2UtI
- MoBBQZLj0vv6cdw9DqzDow5SZW8gyQAGBUmOnZs9DqzDo/h2Ci8gyQAGBUmOUezv54qwUo/cPqjX0efP
- juauri79AYlHkgEMCpIci85XnV/9lmOqqVsr9UjDv1W+8fD3N45P+LZ2O1UeMCQZwKAgyVHr7Or8qipx
- PdYVqLKv7caU4xMCp+y8to0qDwySDGBQkOToSI83/ba+P5wJXbtqc2X/OPgUqjwwSDKAQUGSozCQPbZa
- VHkAkGQAg4IkO9X7fPUg9ziwqHKikWQAg4IkO3Xlr0vpR1INdRyslXttq7/br7dsoPj9A32Ng4UkAxgU
- JDkKh24fGD9sqvz06dPz58/v3r175cqV8+bNmzZtWmZmpvx37ty5y5cvz83NLSsra21tdd5pmZSdezPD
- JciPbW1tFy9ePHToUGFh4YEDB06fPt3Y2CiTOmFLNpdb7vP5Lly4IBuePXu2urq6ubm5s7NTJxyIKsly
- dZWVlXl5eTk5OZ9//rlsu3nz5oKCgoqKipaWloh/HxmwohNR0o3D0QkAXkWSoyNVTj9srONgrR0137he
- ZSnfuXPnli5dOnLkSA2Rtddee23hwoUlJSVOgidxfT2cqqqqwIBcdWlp6fz58+Vi9QqCTJ06de/evdK/
- wLCZhDw/P3/OnDkjRozQbYKMHTt2yZIlR48e7ejo0A2sOUmy3Fp53CCPTlJTU/XscGbOnLlnz56///5b
- NwtVXl4uj3Ks1NfX65xj8uBANzZZvHixDgHwKpIcNSdHCJl0fJzhlAQtd/eVL1++/Pbbb2tMoiE70LJL
- qpdiYd++fTod6uefew5YJjuyEmM9yVpGRobsuwcusJ88IJC90rS0NB2ylZ6eXlRUZP8YImKSr1+//s47
- 7+ipDqSkpGzatEkeNAQ27yePMEaPHq1DJt98843OOSYPO3Rjk59+4su5Aa8jydGpaDo94WiaoYvmdeZ+
- WdHN/CnHxxtOT8TqqXLcz0k+f/58w4YNeucdqzVr1rx48UIv0cQqyVu2bGlsbJTW6s+RyD708ePH9UJ7
- q/bhhx/qeY5J/ltbW/UiTOyTnJeXF3Y/PqIJEyZUVFQErqKfpFrPNpk8eXJUz7fLn9Hqho0dO9bJ0wMA
- BhdJjkLF/dMTjow2FDHsuvhnz55c6/MHX13NGX804S8/x1nlR48eOdlDdWLhwoVWd/1WSc7KypozZ47+
- 4IzsdNbU1MhlPnv2LOZbPn36dKsqWyVZHnN8++23+kNMJJnyd9Cr6eXz+fS8cH777Tedc2DHjh26mYk8
- 7tEhAB5Gkp1y3mNZgSQH3HzkW3ouyzDg+or5dWXZy5w7d67ec7tB9lnDvhXLKsmxmTFjhuxBrlq1Sn+O
- yYIFC8LuhloleerUqfp/8dm/f79eU693331XzzBZt26dDkUij4TGjRunm4WSxwFNTU06B8DDSLIjZ5rK
- nPdYVnCShcTyzP3yOb+8ZRhzd8VQZWnnsmXL9J7bPbt379YrCOJuksXHH3+s/xeH/Px8vX1BrJLsFmnk
- 5cuX9cq6u0tKSvQMk/T0dJvXAoKdOHFCtzGRf2IdAuBtJDmyaHssy5DkgH+6/tl/s+CNEyHHyHR3ba/Z
- ElWVDxw4oHfb1jIzM7Ozs6WyRUVFBQUFW7duXbx48ZgxY/TscEaOHHn37l29jj6uJ9kVsmdpfhd3opMs
- pkyZ0v8M/7Nnz6x2cMWZM2cCYzb8fr/Nk//B+QfgZSQ5gtNNZTF8FjlskgMevni4+eqGCUeja7zz5Xxf
- ua2tzb6sn3zyyfXr18O+Sv38+fMjR45MnjxZR00+++wzHe3jJMkjRoyQK5VLvnTp0oULF+RBwIIFC/Q8
- xzIyMnJycoqLiw8dOiSPJCK+2FxYWKg3sY/zJKelpa1bt668vLypqUn+JlL3W7duye2XRy06YW3Xrl16
- fd3d27Zt01NNVqxYoUPW5J9Jp01mzZrFJ5KBoYIk24mtx7Jskhxw63H9snMfGrZya22vdrSvnJubq3fb
- JqNHjz516pTOWWtvb1+0aJFuEyolJaWlpUXnekVM8owZM27evKnTQcrKyhx+wEmuNC8vz/zysNR9/Pjx
- OmQyd+5cnevjMMkbN258/PixbmNSX19v/2hA9oxl/zgwfOfOHas3S6emppr34w3WrFmj0yby+ECHAHge
- SbZU3vhrzMfqipjkgLPNZ+aemmnY1pW1vWZL2PdY9ZNdOqsnS0eOHHnlyhWdi6Sjo0NSqluGKigo0KFe
- 9kmePn36o0ePdNSkqqpKcqujFmTA/JHlfr///rvVwU+khYZPDEdMslzUr7/+qtPW5MGB7EPrNuEcPXpU
- R7u7P/jgAz3VJPhDX2atra1Wfxx5ICL/0DoHwPNIcnjlTbH3WJbDJIueF5jr92WeeN1wCfGvbdVf21S5
- vLxc77ZNvv/+ex1y5ty5c7plqPfff18netkkecSIEZJMnbOwc+dOnbYg+8c6amH79u06aiI74jrUK2KS
- S0tLdTQS+SdYunSpbmayZMkSnet9MkBPNcnKytKhcOShj86Z7NixQ4cADAUkOYyyxlNxHsvaeZID2l60
- fX11w+sODkIS1bKp8vr16/VuO9Trr7/u8C2+/eQqJk6cqNsHSUtLe/nypQ7ZJnnlypU6ZE12ZG0OXfnG
- G29EPKrGX3/9pdMmhkch9kl2/sGkgAcPHlg98T5mzJj+my1/dvnj6xmh5CGL1UeoZXP53XUulOw6y6+s
- cwCGApJs9GtjafxHsY42yQG3H9/62O0XmLdWbw5b5bfeekvvuUOtWbOmJXpWn6QKft+1TZJtnnAOZvN5
- rbAfuzLw+/2zZ8/WDULJb61DvWySLJ2zOmC1DZuX7W/duqVD3d27du3SU01+/PFHHQpl9RSF+PTTT3UI
- wBBBkkOcunfSkLTYVmxJFv5u/7nmM/85NctwgTGvtZc+7/Ibkyw7r1ZvJnJX8Ad4rJIskXO4X24T9WvX
- rumQLatXdoOfQBY2SV6+fLkOReP27du6vckvv/yiQ93dTU1NVv8u8+bN06FQNm/trq6u1iEAQwRJ/pdb
- PZYVc5ID/un650c3XmBec+nzzq4wT+fKXb/ebSfYwYMH9Sqtgzp16lSdiOT06dO6jUn/W5ftWR0O8733
- 3tOJXjZJDv6NnOvq6rJ6y7fhTXAfffSRnmFy7949Hepz9+5dPc9kzpw5fPYJGHJIsnKxx7LiTHLAo5dt
- X1/dGPMLzFY9FnV1dXrPnWDBvbFK8uzZs3UiksrKSt0m1KhRo6xeMjf44YcfdJtQ8+fP14leNkm+evWq
- DkXJ6pujdu7cqRO9zp8/r2eYyI3XoT7fffednmdy7NgxHQIwdJDkHu72WJYrSZagHrtzeEpMR/tae9my
- x6KmpkbvuRNsz549epXWSTZ/LNjKpUuXdJtQo0ePHrAkB7/0GxWrZ5i3bt2qE706OzutDsBieOxiM5mR
- kRHte/QAeAFJ7u1x3O/nMqw4k+zv9le1XFlQNtdwsQ6XfY+FzcGe3LV37169ymRJss/n06EoWX3seNu2
- bTrRJy8vT88zaWho0CHrv4aQvWcdAjCkDPckn7rnwvurzSueJDc+vfd55QrDBTpfa62fr+73xx9/6J13
- ghUXF+tVJkuSz549q0PR8Pv9VgdUCX4iIaClpcXq0B/BH9bKzs7WU0ONGjXqwYMHOgRgSBnuSc65staQ
- NFdWbEl++k/7ruu5GcfGGC7N+XLSY/H06VO9/zaZPXv2/7mnsrJSrzJZkuzk01ZmT548GTFihF5EqLAH
- vFyxYoWeHWrmzJmBgfb2dkmvnhrKfHRxAEPFcE+yBOzLy6sNYYt/RZvkV/5X/7177M2SyYbLiWqtu7za
- SY8DJk2apHfhoYqKinTCbcmRZHnIEsM7mU+ePKnbm1RVVelQkCtXrujZJoEXsyXk+rPJ9evXAxcCYMjh
- teSeKkvMDHmLczlPsr/bX/Pg6nvl7xguIdoVVY+F1ZcNL1q0SCfclhxJFtF+16HcNqu3W6ekpIT9+FZn
- Z+e0adN0KFTgdWK5wfpzKLkiPvsEDF0kuccrt6vsMMnNHffXXFoV58E7ZUXbY1FcXKz34ia1tbU65Kqk
- SfKsWbOiej/z8ePHdUsTqwOAiKKiIh0KNWPGDJu3ApSUlOj2AIYgkqxe+d2scsQkd3R27Pl918RjYw0b
- xrBi6LH4888/rQ4UJXf6T5480TlnZK/O8GVKZkmTZJGdne3wGuvq6uTm6WYmwe9IN5C/p9WrxVbfYzFp
- 0iT5h9DtAQxBJPlfUmXZZzUEL7Zlk+Quf1fpvf/OOPmGYZPY1pdXYulxwJIlS/S+3ET23pwfyTlwjOvp
- 06fbb5JMSRbLly+P+MDlwoULY8eO1Q1MUkxfKW2wevVqHXUmtreeAfAOkhxC8uZKla2SXNta88GZ+Ybh
- mNeXMe0f97N5D5FIT08/cOCAzYEq/X5/Y2Njbm5u/9ccTZs2zearh5IsyWLixIkHDx7s6OjQzfrIX+bO
- nTsSVPsDia9du1Y3sHDt2jUddSA1NfXhw4e6JYChiSQbuVJlc5L/evaX7NHG/7Jx/4qzxwE2360UIDt5
- q1at+umnnyorK30+X319fXV1dUlJydatW99++20dCiJVbm5u1ksPlXxJDpBrX7x4sTw0KSwszMvL27hx
- o9X3TQWTxzERvzlRfqlZs2bpBpHIIwDdDMCQRZLDiL/KwUl+3vls743vJx0bZ5iJZ7nSYyFVGDdunN6p
- uyQzMzNslZM1ybGRRzl6ZbZ+/vln3SCSmA8rBsA7SHJ4L1+9XH3pM0MIna9Akv3+rl/unZxVOtVwbpxr
- /ZUvXOlxwMWLF13/okap8v379/UK+pDkftnZ2XpNkTx58kR+O93Mmtx+PvsEJAGSbEmyt7pypSGHDpck
- ue5hbdaZhYbT4185V9a42OOA0tJSq2NLxUYK0d7erpfeJwmS/OWXX8qjDf0hVsuWLYvqfdHr16/XLa0F
- f+kygKGLJNuJucrxH/oj7Fpcscj1HgdUVlZafaFvVGSHe+PGjS9fvtTLDZIESS4uLr5161Z6err+HL1N
- mzZF+zkln8+nG1uYMmUKn30CkgNJjiCefWXX1/gjqafundRb5raWlharQys7NHv2bJsjWyVBkisqKuTc
- hoaGN998U09ybPLkyadPnw5cflT8fr/Vwb8C5JfSUQBDHEmOzGtV/iVhVRY1NTVLly6N9nnsRYsWlZeX
- 27+cmQRJrq+vDwx0dHRs3749NTVVz7CVmZkpv3s8X2B87NgxvSwTuQ2PHj3SOQBDHEl2pLfKsb/by90l
- Vfa13dBblhiyx1xcXLxs2TKrb6cQstv3ySefyFjED/ME1NXVFYRz4sQJnYikublZtwm1f/9+h29uqq2t
- 1W1CGY5Def78eT0jiGT16dOnOtGrra1NrjorK8v8VPa4cePefffdbdu2VVVVOXy4YCM/P18v12TdunU6
- BGDoI8lOeafK317bPmBvr5Urkg75fL4LFy7IfrCQ/5Ef5cQBuw3eJ3+Kx48fNzQ0yG703bt329vbXfzj
- dHZ2yqMfLbBJ/447gCRAkqMgVc6u/NQQyAFe39XuoIXDSllZmebX5P3339chAEmBJEenp8oXjVVeejbL
- cEqCFj0ebuSfe8GCBVpgk/Lycp0DkBRIctQM7/YquPGD3G/m39jTf0qC1re12/3d9Hh4sfkEVGZmZvyv
- UgPwFJIci1f+V9m9VS705elJ3d35N34ILqi7q+f1Y3o8/NgcR6ygoECHACQLkhwjqXLFfePHTGWP2ZBS
- V1bP89X0ePhpbW0dOXKkFjhUWlqa+fhoAIY6kuwmv9/vepW/q82lx8OT1aeoRU5Ojg4BSCIk2WXuVpn9
- 42HrxYsXGRkZWuBQr732WkNDg84BSCIk2X1S5X2+vYa4xrDo8XBWWlqqBTbJysrSIQDJhSQnRPxV3nWd
- 56uHr66urnnz5mmBTc6ePatzAJILSU6U3mewY6zyLl4/Ht7q6uo0vybTpk3js09AsiLJCSRZ7anyYWNx
- 7Rc9RnZ2thbYpKioSIcAJB2SnFgS132+POdVpsf4+++/U1JStMCh0tLSDF99ASCZkOSEc17l3dd3+jle
- 5rDX1NR0woLN11EDSAIkeSBIaAt9+YYAGxY9BoBhjiQPEPsq02MAAEkeOBLdopsF5mewv6vN1QkAwDBG
- kgfa/pv7gnvc830S7B8DAEjyoPixvlB7XEuPAQCKJA+OH28W9hwvkx4DAPqQZAAAPIEkAwDgCSQZAABP
- IMkAAHgCSQYAwBNIMgAAnkCSAQDwBJIMAIAnkGQAADyBJAMA4AkkGQAATyDJAAB4AkkGAMATSDIAAJ5A
- kgEA8ASSDACAJ5BkAAA8gSQDAOAJJBkAAE8gyQAAeAJJBgDAE0gyAACeQJIBAPAEkgwAgCeQZAAAPIEk
- AwDgCSQZAABPIMkAAHgCSQYAwBNIMgAAnkCSAQDwBJIMAIAnkGQAADygu/v/AQJ6ksi8ac+pAAAAAElF
- TkSuQmCC
-</value>
- </data>
- <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>
- AAABAAgAMDAAAAEAIACoJQAAhgAAADAwAAABAAgAqA4AAC4mAAAgIAAAAQAgAKgQAADWNAAAICAAAAEA
- CACoCAAAfkUAABgYAAABACAAiAkAACZOAAAYGAAAAQAIAMgGAACuVwAAEBAAAAEAIABoBAAAdl4AABAQ
- AAABAAgAaAUAAN5iAAAoAAAAMAAAAGAAAAABACAAAAAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu2UkpItlMxAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7RQM0u1Uv9LtVL/S7NSJQAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKtFIfS7VS/0u1
- Uv9LtVL/S7VS/0i2SAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuz
- Uz1LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9ItkgHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAS7NRTku1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7NSJQAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLs1FOS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0i2
- UzEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuzUz1LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv/OtgAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASrRSH0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/xsYACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtFAzS7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqqAAMAAAAAAAAAAEu2UkpLtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv/atgAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv/MuwAPTLVSUEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/0rwAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9K8ABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/atgAOAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/8Dkwf9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////bsBk/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////
- /////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////
- ///////////////////////////////////d6sP/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/////////////////////////////////////////////////0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////////////////////////////
- ////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////////////
- //////////////////////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////
- ////////////////////////////////////////////////////////0uOr/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAA/wABS7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////
- ////////////////////////////////////////////////////////////////////////V7lZ/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/////////////////////////////////////////////////////////////////7Dd
- rP9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////////////////////////////
- ////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9ItlMxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////////////
- /////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0uzUiUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////
- //////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SLZIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAD/AAFLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////
- ////////////////////////+fff/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9ItkgHAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS//////////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9S4ABIAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/1L8AGAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/1q6Xf9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv/OtgAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/8bGAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/atgAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9K8ABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtFHOAAAAAEu1Uv9LtVL/0rwAFwAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0UdQAAAAAAAAAAAAAAADatgAOAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SrVS7AAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0
- UuIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/SrVS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9KtVLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0UuIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9KtVLsAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEq0UdQAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD///+f//8AAP///w///wAA///+B///
- AAD///wD//8AAP//+AH//wAA///wAP//AAD//+AAf/8AAP//wAA//wAA//+AAB//AAD/8wAAD/8AAP/g
- AAAH/wAA/8AAAAP/AAD/gAAAA/8AAP8AAAAH/wAA/gAAAAP/AAD8AAAAAf8AAPgAAAAA/wAA8AAAAAB/
- AADgAAAAAD8AAMAAAAAAHwAAgAAAAAAPAACAAAAAAAcAAMAAAAAAAwAA4AAAAAABAADwAAAAAAAAAPgA
- AAAAAQAA/AAAAAADAAD+AAAAAAcAAP8AAAAADwAA/4AAAAAfAAD/wAAAAD8AAP/gAAAAfwAA/+AAAAD/
- AAD/wAAAAf8AAP/AAAAD/wAA/+AAAAf/AAD/8AAAT/8AAP/4AAD//wAA//wAAf//AAD//gAD//8AAP//
- AAf//wAA//+AD///AAD//8Af//8AAP//4D///wAA///wf///AAD///j///8AAP///f///wAAKAAAADAA
- AABgAAAAAQAIAAAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAP9vtzz/cLc8/1+2Rv9RtU//S7VS/0y1
- Uf9NtVH/TrVQ/0+1UP9NtlP/TbZU/062VP9Rt1f/VLla/1W5W/9WuVz/Y7ZD/2C2Rf9htkX/YrZE/2O2
- RP9otkD/Yb5m/2S/aP9mv2j/aMBq/3jHef95x3z/e8h8/3zIfP9+yX//f8qD/4PMiP+R0ZL/ktGS/5TS
- lP+V0pX/mdSY/6jaqP+s3Kv/r92t/7Ddrv/G5sL/xufF/8jnxv/L6cj/3fDb/9/x3f/g8d3/4/Pg/+/4
- 7f/x+e//8vnv//r8+P/7/fn//v/+/////v8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAABQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUFAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFBQUFBQAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAUFBQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUF
- BQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUFBQUFBQMA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAUEgAABQUFBQUFBQUFBQUFBQUFBQUFBgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBhIFBQUFBQUF
- BQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQYFBQUFBQUFBQUFBQUFBQUFBQUF
- BQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBhIAAAAAAAAA
- AAAAAAAAAAAAABUHBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCBUAAAAAAAAAAAAAAAAAAAAAFQcF
- BQUFBQUFBQUGBQUFBQUFBQUFBQUFBQUFBQUFEwAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUgKg8F
- BQUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAAAAAAAABUHBQUFBQUFBQUFBQUhOTUkDAUFBQUFBQUFBQUF
- BQUFBQUFAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQUhOTk5MR4FBQUFBQUFBQUFBQUFBQUFBQAAAAAA
- AAAAAAASBwUFBQUFBQUFBQUFBQUhOTk5OTgsGAUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAUFBQUFBQUF
- BQUFBQUFBQUhOTk5OTk5NygOBQUFBQUFBQUFBQUFBQUFAAAAAAAABQUFBQUFBQUFBQUFBQUFBQUhOTk5
- OTk5OTkzIwsFBQUFBQUFBQUFBQUFBQAAAAAFBQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OS8bBQUF
- BQUFBQUFBQUFBQUAAAAFBQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OTk4KxcFBQUFBQUFBQUFBQUF
- AAAABQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OTk5OTYnDQUFBQUFBQUFBQUFBQAAAAUFBQUFBQUF
- BQUFBQUFBQUhOTk5OTk5OTk5OTk5OTMiCwUFBQUFBQUFBQUFBQUAAAAFBQUFBQUFBQUFBQUFBQUhOTk5
- OTk5OTk5OTk2KQ4FBQUFBQUFBQUFBQUFBQUAAAAABQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OS0ZBQUF
- BQUFBQUFBQUFBQUFBQUAAAAAAAUFBQUFBQUFBQUFBQUhOTk5OTk5OTkwHQUFBQUFBQUFBQUFBQUFBQUF
- BQAAAAAAAAAFBQUFBQUFBQUFBQUhOTk5OTk5NCUKBQUFBQUFBQUFBQUFBQUFBQUFAAAAAAAAAAAABQUF
- BQUFBQUFBQUhOTk5OTcpEAUFBQUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAAAAAUFBQUFBQUFBQUhOTk5
- LhoFBQUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAFBQUFBQUFBQUhOTIfBQUFBQUFBQUFBQUF
- BQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUcJgsFBQUFBQUFBQUFBQUFBQUFBQUFBgMAAAAA
- AAAAAAAAAAAAAAAAFgcFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAAAAAW
- CQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAAAAARBwUFBQUFBQUFBQUF
- BQUFBQUFBQUFBQUFBQUFBgMAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQUFBQUFBQUFBQUH
- BQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBQUFBQUFBQUFBQUFBQUFBQUFBQUBBAYDAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAUBwUFBQUFBQUFBQUFBQUFBQUFBQUAAhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAFAcFBQUFBQUFBQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQHBQUFBQUF
- BQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUFBQUAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAABQHBQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAVBwUFBQUFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcF
- BQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBQUFAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVBwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAD///////8AAP///5///wAA////D///AAD///4H//8AAP///AP//wAA///4Af//AAD///AA//8AAP//
- 4AB//wAA///AAD//AAD//4AAH/8AAP/zAAAP/wAA/+AAAAf/AAD/wAAAA/8AAP+AAAAD/wAA/wAAAAf/
- AAD+AAAAA/8AAPwAAAAB/wAA+AAAAAD/AADwAAAAAH8AAOAAAAAAPwAAwAAAAAAfAACAAAAAAA8AAIAA
- AAAABwAAwAAAAAADAADgAAAAAAEAAPAAAAAAAAAA+AAAAAABAAD8AAAAAAMAAP4AAAAABwAA/wAAAAAP
- AAD/gAAAAB8AAP/AAAAAPwAA/+AAAAB/AAD/4AAAAP8AAP/AAAAB/wAA/8AAAAP/AAD/4AAAB/8AAP/w
- AABP/wAA//gAAP//AAD//AAB//8AAP/+AAP//wAA//8AB///AAD//4AP//8AAP//wB///wAA///gP///
- AAD///B///8AAP//+P///wAA///9////AAAoAAAAIAAAAEAAAAABACAAAAAAAAAgAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtFH+S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv/8/fz/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P///////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVH/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL//P38//////////////////////9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/8/fz/////////////////////////
- ///q8dP/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P//////////////
- ////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL//P38////
- /////////////////////////////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv/8/fz/////////////////////////////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS//z9/P//////////////////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL//P38////////////////////////////mspu/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/8/fz//////////////////v7+/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P///////////0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL//P38/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAEu1
- Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu0Uf5LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uf9LtVL/S7VS/0u1
- Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//v////x////4P///8B///+AP///AB//9gAP/+AAB//AAAP/gA
- AH/wAAB/4AAAP8AAAB+AAAAPAAAAB4AAAAPAAAAB4AAAAPAAAAH4AAAD/AAAB/4AAA/+AAAf/AAAP/4A
- AH//AAb//4AP///AH///4D////B////4/////f//KAAAACAAAABAAAAAAQAIAAAAAAAACAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAP95tzb/erc2/3u3Nf9wtzz/VrZL/1C1T/9RtU7/UrVO/1O1Tf9VtUz/VbZM/0u1
- Uv9NtVH/TrVQ/0+1UP9NtlT/ULdW/1a5W/9gtkb/Y7ZE/2i2QP9bu2D/Zb9p/2vCb/9vw3L/d8d6/33J
- fv+NuCr/jrgq/4+4Kf+SuSf/k7kn/5C5Kf+SuSj/hcyG/4fNif+U0pX/l9OY/5bUmf+e15//n9eh/63d
- rv+s3bD/uOG5/8jox//Q68//0+zS/97x3f/h8t//5vTl//D47//1+/T/+/36//3+/f//////AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAADAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwMDAYCAAAAAAAAAAAAAAAAAAAA
- AAAAAAAMDAwMDAwMDAwMDAcBAAAAAAAAAAAAAAAAAAAAIAsTDAwMDAwMDAwMDAwMDAcDAAAAAAAAAAAA
- AAAAACAJDA4MDAwMDAwMDAwMDAwMDAcDAAAAAAAAAAAAAAAdCQwMDAwMDAwMDAwMDAwMDAwMDAoAAAAA
- AAAAAAAAHgkMDAwMDAwMDAwMDAwMDAwMDAwNFAAAAAAAAAAAACAJDAwMDAwMDCkmEAwMDAwMDAwMDAwM
- AAAAAAAAAAAhCQwMDAwMDAwMKzcxGwwMDAwMDAwMDAwMAAAAAAAADAcMDAwMDAwMDAwrNzc3LRcMDAwM
- DAwMDAwMAAAAAAwMDAwMDAwMDAwMDCs3Nzc3NSoSDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMKzc3Nzc3
- NzMlEAwMDAwMDAwMAAAMDAwMDAwMDAwMDAwrNzc3Nzc3NzcwGgwMDAwMDAwMAAAMDAwMDAwMDAwMDCs3
- Nzc3Nzc3Ny4YDAwMDAwMDAwMAAAMDAwMDAwMDAwMKzc3Nzc3NzIjDAwMDAwMDAwMDAwAAAAMDAwMDAwM
- DAwrNzc3NzQoEQwMDAwMDAwMDAwMDAAAAAAMDAwMDAwMDCs3NzYsFgwMDAwMDAwMDAwMDAwAAAAAAAAM
- DAwMDAwMKzcvGQwMDAwMDAwMDAwMDAcCAAAAAAAAAAAMDAwMDAwnJAwMDAwMDAwMDAwMDAwHAwAAAAAA
- AAAAABUPDAwMDAwMDAwMDAwMDAwMDAwMBwIAAAAAAAAAAAAABQwMDAwMDAwMDAwMDAwMDAwMDAcBAAAA
- AAAAAAAAAAAfCQwMDAwMDAwMDAwMDAwMDwwHAgAAAAAAAAAAAAAAAAAfCQwMDAwMDAwMDAwMDAwEBQIA
- AAAAAAAAAAAAAAAAAAAcCQwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAeCQwMDAwMDAwMDAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAgCQwMDAwMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeCQwMDAwM
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdCQwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi
- CAwAAAAAAAAAAAAAAAAAAAAA//+/////H////g////wH///4A///8AH//2AA//4AAH/8AAA/+AAAf/AA
- AH/gAAA/wAAAH4AAAA8AAAAHgAAAA8AAAAHgAAAA8AAAAfgAAAP8AAAH/gAAD/4AAB/8AAA//gAAf/8A
- Bv//gA///8Af///gP///8H////j////9//8oAAAAGAAAADAAAAABACAAAAAAAAASAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASrRSQUm1
- Ty0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJtU8tS7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuz
- UVFLtVL/S7VS/0u1Uv9LtVL/SbVPLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASbVPLUu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/8+/
- ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABKtFJBS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/UqgAGAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/2S1QV5LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/1L8AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL//////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/////////////////////////
- ///29+b/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL//////////////////////////////////////0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/////////
- /////////////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////////////////////////+l1ZP/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9JtU8tAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL//////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL///////////9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/0LkAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv/PvwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9SqAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u0
- UtJLtVL/1L8AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SrVR3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9KtVLyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u0Uc4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1
- Uv9LtVL/SrVS8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKtVHeAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP/z/wD/4f8A/8D/AP+AfwD9AD8A+AAfAPAA
- HwDgAA8AwAAHAIAAAwCAAAEAwAAAAOAAAQDwAAMA+AAHAPwADwD4AB8A/AA/AP4A/wD/Af8A/4P/AP/H
- /wD/7/8AKAAAABgAAAAwAAAAAQAIAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9UtUz/VbZM/0u1
- Uv9MtVH/TbVR/061UP9PtVD/TLZT/062VP9SuFj/Vrlc/2W2Qv9htkX/Xr1j/2XAav9qwW7/ccR1/3LF
- dv99yYD/iM6L/5PSlv+j2aX/r92w/73kvv/B5sP/yejJ/9ju1//h8uD/6vbp//H58P/3/Pb/+/36//7+
- /v//////AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAA
- AAADAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAAAAAAAA
- AAAAAAAAAAAAAAADAwMDAwMDAQAAAAAAAAAAAAAAAAANAAMDAwMDAwMDAwEAAAAAAAAAAAAAAAIEBgMD
- AwMDAwMDAwMBAAAAAAAAAAAAAgQDAwMDAwMDAwMDAwMEDQAAAAAAAAACBAMDAwMRCQMDAwMDAwMFAwAA
- AAAAAAIEAwMDAwMZHBMDAwMDAwMDAwMAAAAABwQDAwMDAwMZIiIaDwMDAwMDAwMDAAADAwMDAwMDAwMZ
- IiIiIBcLAwMDAwMDAwADAwMDAwMDAwMZIiIiIiIeFQgDAwMDAwMAAwMDAwMDAwMZIiIiIiIdFAMDAwMD
- AwMAAAMDAwMDAwMZIiIiHxYKAwMDAwMDAwMAAAADAwMDAwMZIiEYDgMDAwMDAwMDAwAAAAAAAwMDAwMZ
- GxIDAwMDAwMDAwMBAAAAAAAAAAYDAwMQBAMDAwMDAwMDAwEAAAAAAAAADAUDAwMDAwMDAwMDAwMDAQAA
- AAAAAAAAAAIEAwMDAwMDAwMDBQUBAAAAAAAAAAAAAAACBAMDAwMDAwMDAwwAAAAAAAAAAAAAAAAAAgQD
- AwMDAwMDAAAAAAAAAAAAAAAAAAAAAAIEAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAACBAMDAwAAAAAAAAAA
- AAAAAAAAAAAAAAAAAgQDAAAAAAAAAAAAAAD///8A//P/AP/h/wD/wP8A/4B/AP0APwD4AB8A8AAfAOAA
- DwDAAAcAgAADAIAAAQDAAAAA4AABAPAAAwD4AAcA/AAPAPgAHwD8AD8A/gD/AP8B/wD/g/8A/8f/AP/v
- /wAoAAAAEAAAACAAAAABACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv//////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA
- AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/////////////////S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv8AAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////S7VS/0u1
- Uv9LtVL/S7VS/wAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv//////////////////////sN2s/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/////////////////S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS//////9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1
- Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1
- Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABLtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAEu1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9/AAD+PwAA/B8AAPAP
- AADgBwAAwAcAAIADAAAAAQAAgAAAAMABAADgAwAA4AcAAPAPAAD4PwAA/H8AAP7/AAAoAAAAEAAAACAA
- AAABAAgAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0+1T/9QtU//VLVM/1W2TP9LtVL/TLVR/021
- Uf9PtVD/U7hZ/1a5XP9hvmb/ZsBr/3bGev99yYH/qNuq/6/esf+y37T/uOK6/8Lmw//K6cr/1u7X//n8
- +f/7/fv////+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
- AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAFBQUAAAAAAAAAAAAAAAAFBQUF
- BQAAAAAAAAAAAAAFBQUFBQYDAAAAAAAAAAQBBQUFBQUFBgMAAAAAAAQGBQUFBQUFBQUIAAAAAAQGBQUF
- Eg4FBQUFBQUAAAUGBQUFBRUYFAwFBQUFBQAFBQUFBQUVGBgXEAoFBQUFBQUFBQUFFRgYFg8JBQUFBQAF
- BQUFBRUYEwsFBQUFBQUAAAUFBQURDQUFBQUFBgMAAAAAAgUFBQUFBQUFBgMAAAAAAAQGBQUFBQUFAQMA
- AAAAAAAABAYFBQUFBQAAAAAAAAAAAAAEBgUFBQAAAAAAAAAAAAAAAAQHBQAAAAAAAAD/fwAA/j8AAPwf
- AADwDwAA4AcAAMAHAACAAwAAAAEAAIAAAADAAQAA4AMAAOAHAADwDwAA+D8AAPx/AAD+/wAA
-</value>
- </data>
-</root> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/SplashLogo2.png b/MediaBrowser.ServerApplication/SplashLogo2.png
deleted file mode 100644
index abb73577e..000000000
--- a/MediaBrowser.ServerApplication/SplashLogo2.png
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs b/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs
deleted file mode 100644
index aa8679bd5..000000000
--- a/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Logging;
-using System.Diagnostics;
-using System.IO;
-
-namespace MediaBrowser.ServerApplication.Updates
-{
- /// <summary>
- /// Update the specified application using the specified archive
- /// </summary>
- public class ApplicationUpdater
- {
- private const string UpdaterExe = "Mediabrowser.Updater.exe";
- private const string UpdaterDll = "Mediabrowser.InstallUtil.dll";
- public void UpdateApplication(IApplicationPaths appPaths, string archive, ILogger logger, string restartServiceName)
- {
- // First see if there is a version file and read that in
- var version = "Unknown";
- if (File.Exists(archive + ".ver"))
- {
- version = File.ReadAllText(archive + ".ver");
- }
-
- var systemPath = appPaths.ProgramSystemPath;
- var tempPath = Path.GetTempPath();
-
- // Use our installer passing it the specific archive
- // We need to copy to a temp directory and execute it there
- var source = Path.Combine(systemPath, UpdaterExe);
-
- logger.Info("Copying updater to temporary location");
- var tempUpdater = Path.Combine(tempPath, UpdaterExe);
- File.Copy(source, tempUpdater, true);
- source = Path.Combine(systemPath, UpdaterDll);
- var tempUpdaterDll = Path.Combine(tempPath, UpdaterDll);
-
- logger.Info("Copying updater dependencies to temporary location");
- File.Copy(source, tempUpdaterDll, true);
- var product = "server";
-
- logger.Info("Starting updater process.");
-
- // installpath = program data folder
- // startpath = executable to launch
- // systempath = folder containing installation
- var args = string.Format("product={0} archive=\"{1}\" caller={2} pismo=false version={3} service={4} installpath=\"{5}\" startpath=\"{6}\" systempath=\"{7}\"",
- product, archive, Process.GetCurrentProcess().Id, version, restartServiceName ?? string.Empty, appPaths.ProgramDataPath, MainStartup.ApplicationPath, systemPath);
-
- logger.Info("Args: {0}", args);
-
- Process.Start(new ProcessStartInfo
- {
- FileName = tempUpdater,
- Arguments = args,
- UseShellExecute = false,
- CreateNoWindow = true,
- ErrorDialog = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- WorkingDirectory = Path.GetDirectoryName(tempUpdater)
- });
-
- // That's it. The installer will do the work once we exit
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/WindowsAppHost.cs b/MediaBrowser.ServerApplication/WindowsAppHost.cs
deleted file mode 100644
index 9896b75e3..000000000
--- a/MediaBrowser.ServerApplication/WindowsAppHost.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Reflection;
-using System.Runtime.InteropServices.ComTypes;
-using Emby.Server.CinemaMode;
-using Emby.Server.Connect;
-using Emby.Server.Implementations;
-using Emby.Server.Implementations.EntryPoints;
-using Emby.Server.Implementations.FFMpeg;
-using Emby.Server.Implementations.IO;
-using Emby.Server.Sync;
-using MediaBrowser.Controller.Connect;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.System;
-using MediaBrowser.Model.Updates;
-using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.ServerApplication.Native;
-
-namespace MediaBrowser.ServerApplication
-{
- public class WindowsAppHost : ApplicationHost
- {
- public WindowsAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager)
- : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, networkManager)
- {
- fileSystem.AddShortcutHandler(new LnkShortcutHandler());
- }
-
- protected override IConnectManager CreateConnectManager()
- {
- return new ConnectManager();
- }
-
- protected override ISyncManager CreateSyncManager()
- {
- return new SyncManager();
- }
-
- protected override void RestartInternal()
- {
- MainStartup.Restart();
- }
-
- public override void EnableLoopback(string appName)
- {
- LoopUtil.Run(appName);
- }
-
- protected override List<Assembly> GetAssembliesWithPartsInternal()
- {
- var list = new List<Assembly>();
-
- list.Add(typeof(DefaultIntroProvider).Assembly);
- list.Add(typeof(ConnectManager).Assembly);
- list.Add(typeof(SyncManager).Assembly);
- list.Add(GetType().Assembly);
-
- return list;
- }
-
- protected override void ShutdownInternal()
- {
- MainStartup.Shutdown();
- }
-
- protected override void AuthorizeServer()
- {
- ServerAuthorization.AuthorizeServer(UdpServerEntryPoint.PortNumber,
- ServerConfigurationManager.Configuration.HttpServerPortNumber,
- ServerConfigurationManager.Configuration.HttpsPortNumber,
- MainStartup.ApplicationPath,
- ConfigurationManager.CommonApplicationPaths.TempDirectory);
- }
-
- protected override void ConfigureAutoRunInternal(bool autorun)
- {
- var startupPath = Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
-
- if (autorun && !MainStartup.IsRunningAsService)
- {
- //Copy our shortut into the startup folder for this user
- var targetPath = Path.Combine(startupPath, "Emby Server.lnk");
-
- IShellLinkW link = (IShellLinkW)new ShellLink();
-
- var appPath = Process.GetCurrentProcess().MainModule.FileName;
-
- // setup shortcut information
- link.SetDescription(Name);
- link.SetPath(appPath);
- link.SetWorkingDirectory(Path.GetDirectoryName(appPath));
-
- // save it
- IPersistFile file = (IPersistFile)link;
- file.Save(targetPath, true);
- }
- else
- {
- //Remove our shortcut from the startup folder for this user
- FileSystemManager.DeleteFile(Path.Combine(startupPath, "Emby Server.lnk"));
- }
- }
-
- public override bool CanSelfRestart
- {
- get
- {
- return MainStartup.CanSelfRestart;
- }
- }
-
- public override bool CanSelfUpdate
- {
- get
- {
- return MainStartup.CanSelfUpdate;
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/app.manifest b/MediaBrowser.ServerApplication/app.manifest
deleted file mode 100644
index d040f5fb9..000000000
--- a/MediaBrowser.ServerApplication/app.manifest
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
- <security>
- <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
- <!-- UAC Manifest Options
- If you want to change the Windows User Account Control level replace the
- requestedExecutionLevel node with one of the following.
-
- <requestedExecutionLevel level="asInvoker" uiAccess="false" />
- <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
- <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
-
- Specifying requestedExecutionLevel node will disable file and registry virtualization.
- If you want to utilize File and Registry Virtualization for backward
- compatibility then delete the requestedExecutionLevel node.
- -->
- </requestedPrivileges>
- <applicationRequestMinimum>
- <defaultAssemblyRequest permissionSetReference="Custom" />
- <PermissionSet ID="Custom" SameSite="site" Unrestricted="true" />
- </applicationRequestMinimum>
- </security>
- </trustInfo>
- <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
- <application>
- <!-- A list of all Windows versions that this application is designed to work with.
- Windows will automatically select the most compatible environment.-->
- <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
- <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->
- <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
- <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->
- <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
- <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->
- </application>
- </compatibility>
- <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
- <!-- <dependency>
- <dependentAssembly>
- <assemblyIdentity
- type="win32"
- name="Microsoft.Windows.Common-Controls"
- version="6.0.0.0"
- processorArchitecture="*"
- publicKeyToken="6595b64144ccf1df"
- language="*"
- />
- </dependentAssembly>
- </dependency>-->
-</asmv1:assembly> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
deleted file mode 100644
index e40a3ab15..000000000
--- a/MediaBrowser.ServerApplication/packages.config
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
- <package id="SharpCompress" version="0.18.2" targetFramework="net462" />
- <package id="SimpleInjector" version="4.0.12" targetFramework="net462" />
- <package id="SkiaSharp" version="1.58.1" 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.WebDashboard/Api/ConfigurationPageInfo.cs b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
index b69c14fee..4cb2444da 100644
--- a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
+++ b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
@@ -12,6 +12,8 @@ namespace MediaBrowser.WebDashboard.Api
/// <value>The name.</value>
public string Name { get; set; }
public bool EnableInMainMenu { get; set; }
+ public string MenuSection { get; set; }
+ public string MenuIcon { get; set; }
public string DisplayName { get; set; }
@@ -45,6 +47,8 @@ namespace MediaBrowser.WebDashboard.Api
{
Name = page.Name;
EnableInMainMenu = page.EnableInMainMenu;
+ MenuSection = page.MenuSection;
+ MenuIcon = page.MenuIcon;
DisplayName = string.IsNullOrWhiteSpace(page.DisplayName) ? plugin.Name : page.DisplayName;
// Don't use "N" because it needs to match Plugin.Id
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 47662b509..f0c352157 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -119,15 +119,12 @@ namespace MediaBrowser.WebDashboard.Api
private readonly ILocalizationManager _localization;
private readonly IJsonSerializer _jsonSerializer;
private readonly IAssemblyInfo _assemblyInfo;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private IResourceFileManager _resourceFileManager;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardService" /> class.
/// </summary>
- /// <param name="appHost">The app host.</param>
- /// <param name="serverConfigurationManager">The server configuration manager.</param>
- /// <param name="fileSystem">The file system.</param>
- public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo, ILogger logger, IHttpResultFactory resultFactory, IMemoryStreamFactory memoryStreamFactory)
+ public DashboardService(IServerApplicationHost appHost, IResourceFileManager resourceFileManager, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo, ILogger logger, IHttpResultFactory resultFactory)
{
_appHost = appHost;
_serverConfigurationManager = serverConfigurationManager;
@@ -137,7 +134,7 @@ namespace MediaBrowser.WebDashboard.Api
_assemblyInfo = assemblyInfo;
_logger = logger;
_resultFactory = resultFactory;
- _memoryStreamFactory = memoryStreamFactory;
+ _resourceFileManager = resourceFileManager;
}
/// <summary>
@@ -267,7 +264,7 @@ namespace MediaBrowser.WebDashboard.Api
configPages = configPages.Where(p => p.EnableInMainMenu == request.EnableInMainMenu.Value).ToList();
}
- return _resultFactory.GetOptimizedResult(Request, configPages);
+ return configPages;
}
private IEnumerable<Tuple<PluginPageInfo, IPlugin>> GetPluginPages()
@@ -309,19 +306,18 @@ namespace MediaBrowser.WebDashboard.Api
{
var path = request.ResourceName;
- path = path.Replace("bower_components" + _appHost.ApplicationVersion, "bower_components", StringComparison.OrdinalIgnoreCase);
-
var contentType = MimeTypes.GetMimeType(path);
var basePath = DashboardUIPath;
// Bounce them to the startup wizard if it hasn't been completed yet
if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted &&
- path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 && GetPackageCreator(basePath).IsCoreHtml(path))
+ Request.RawUrl.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 &&
+ GetPackageCreator(basePath).IsCoreHtml(path))
{
// But don't redirect if an html import is being requested.
if (path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
{
- Request.Response.Redirect("wizardstart.html");
+ Request.Response.Redirect("index.html?start=wizard#!/wizardstart.html");
return null;
}
}
@@ -335,7 +331,7 @@ namespace MediaBrowser.WebDashboard.Api
!contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
{
var stream = await GetResourceStream(basePath, path, localizationCulture).ConfigureAwait(false);
- return _resultFactory.GetResult(stream, contentType);
+ return _resultFactory.GetResult(Request, stream, contentType);
}
TimeSpan? cacheDuration = null;
@@ -355,7 +351,7 @@ namespace MediaBrowser.WebDashboard.Api
return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
}
- return await _resultFactory.GetStaticFileResult(Request, GetPackageCreator(basePath).GetResourcePath(path));
+ return await _resourceFileManager.GetStaticFileResult(Request, basePath, path, contentType, cacheDuration);
}
private string GetLocalizationCulture()
@@ -374,7 +370,7 @@ namespace MediaBrowser.WebDashboard.Api
private PackageCreator GetPackageCreator(string basePath)
{
- return new PackageCreator(basePath, _fileSystem, _logger, _serverConfigurationManager, _memoryStreamFactory);
+ return new PackageCreator(basePath, _fileSystem, _logger, _serverConfigurationManager, _resourceFileManager);
}
public async Task<object> Get(GetDashboardPackage request)
@@ -405,16 +401,14 @@ namespace MediaBrowser.WebDashboard.Api
CopyDirectory(inputPath, targetPath);
}
- string culture = null;
-
var appVersion = _appHost.ApplicationVersion.ToString();
- await DumpHtml(packageCreator, inputPath, targetPath, mode, culture, appVersion);
+ await DumpHtml(packageCreator, inputPath, targetPath, mode, appVersion);
return "";
}
- private async Task DumpHtml(PackageCreator packageCreator, string source, string destination, string mode, string culture, string appVersion)
+ private async Task DumpHtml(PackageCreator packageCreator, string source, string destination, string mode, string appVersion)
{
foreach (var file in _fileSystem.GetFiles(source))
{
@@ -425,13 +419,13 @@ namespace MediaBrowser.WebDashboard.Api
continue;
}
- await DumpFile(packageCreator, filename, Path.Combine(destination, filename), mode, culture, appVersion).ConfigureAwait(false);
+ await DumpFile(packageCreator, filename, Path.Combine(destination, filename), mode, appVersion).ConfigureAwait(false);
}
}
- private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion)
+ private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string appVersion)
{
- using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, culture, appVersion).ConfigureAwait(false))
+ using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, null, appVersion).ConfigureAwait(false))
{
using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 5fd94e0d7..619d0660f 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Controller;
namespace MediaBrowser.WebDashboard.Api
{
@@ -19,16 +20,16 @@ namespace MediaBrowser.WebDashboard.Api
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly string _basePath;
+ private IResourceFileManager _resourceFileManager;
- public PackageCreator(string basePath, IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IMemoryStreamFactory memoryStreamFactory)
+ public PackageCreator(string basePath, IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IResourceFileManager resourceFileManager)
{
_fileSystem = fileSystem;
_logger = logger;
_config = config;
- _memoryStreamFactory = memoryStreamFactory;
_basePath = basePath;
+ _resourceFileManager = resourceFileManager;
}
public async Task<Stream> GetResource(string virtualPath,
@@ -63,32 +64,6 @@ namespace MediaBrowser.WebDashboard.Api
return Path.GetExtension(path).EndsWith(format, StringComparison.OrdinalIgnoreCase);
}
- /// <summary>
- /// Gets the dashboard resource path.
- /// </summary>
- /// <returns>System.String.</returns>
- public string GetResourcePath(string virtualPath)
- {
- var fullPath = Path.Combine(_basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
-
- try
- {
- fullPath = _fileSystem.GetFullPath(fullPath);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in Path.GetFullPath", ex);
- }
-
- // Don't allow file system access outside of the source folder
- if (!_fileSystem.ContainsSubPath(_basePath, fullPath))
- {
- throw new SecurityException("Access denied");
- }
-
- return fullPath;
- }
-
public bool IsCoreHtml(string path)
{
if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1)
@@ -96,11 +71,7 @@ namespace MediaBrowser.WebDashboard.Api
return false;
}
- path = GetResourcePath(path);
- var parent = _fileSystem.GetDirectoryName(path);
-
- return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(Path.Combine(_basePath, "offline"), parent, StringComparison.OrdinalIgnoreCase);
+ return IsFormat(path, "html");
}
/// <summary>
@@ -109,11 +80,13 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>Task{Stream}.</returns>
public async Task<Stream> ModifyHtml(string path, Stream sourceStream, string mode, string appVersion, string localizationCulture)
{
+ var isMainIndexPage = string.Equals(path, "index.html", StringComparison.OrdinalIgnoreCase);
+
using (sourceStream)
{
string html;
- using (var memoryStream = _memoryStreamFactory.CreateNew())
+ using (var memoryStream = new MemoryStream())
{
await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
@@ -121,38 +94,21 @@ namespace MediaBrowser.WebDashboard.Api
html = Encoding.UTF8.GetString(originalBytes, 0, originalBytes.Length);
- if (!string.IsNullOrWhiteSpace(mode))
- {
- }
- else if (!string.IsNullOrWhiteSpace(path) && !string.Equals(path, "index.html", StringComparison.OrdinalIgnoreCase))
+ if (isMainIndexPage)
{
- var index = html.IndexOf("<body", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
+ if (!string.IsNullOrWhiteSpace(localizationCulture))
{
- html = html.Substring(index);
+ var lang = localizationCulture.Split('-').FirstOrDefault();
- html = html.Substring(html.IndexOf('>') + 1);
-
- index = html.IndexOf("</body>", StringComparison.OrdinalIgnoreCase);
- if (index != -1)
- {
- html = html.Substring(0, index);
- }
+ html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
}
- var mainFile = _fileSystem.ReadAllText(GetResourcePath("index.html"));
-
- html = ReplaceFirst(mainFile, "<div class=\"mainAnimatedPages skinBody\"></div>", "<div class=\"mainAnimatedPages skinBody hide\">" + html + "</div>");
- }
-
- if (!string.IsNullOrWhiteSpace(localizationCulture))
- {
- var lang = localizationCulture.Split('-').FirstOrDefault();
-
- html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
}
}
- html = html.Replace("<head>", "<head>" + GetMetaTags(mode));
+ if (isMainIndexPage)
+ {
+ html = html.Replace("<head>", "<head>" + GetMetaTags(mode));
+ }
// Disable embedded scripts from plugins. We'll run them later once resources have loaded
if (html.IndexOf("<script", StringComparison.OrdinalIgnoreCase) != -1)
@@ -161,29 +117,22 @@ namespace MediaBrowser.WebDashboard.Api
html = html.Replace("</script>", "</script>-->");
}
- html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>");
+ if (isMainIndexPage)
+ {
+ html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>");
+ }
var bytes = Encoding.UTF8.GetBytes(html);
- return _memoryStreamFactory.CreateNew(bytes);
+ return new MemoryStream(bytes);
}
}
- public string ReplaceFirst(string text, string search, string replace)
- {
- int pos = text.IndexOf(search, StringComparison.OrdinalIgnoreCase);
- if (pos < 0)
- {
- return text;
- }
- return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
- }
-
/// <summary>
/// Gets the meta tags.
/// </summary>
/// <returns>System.String.</returns>
- private static string GetMetaTags(string mode)
+ private string GetMetaTags(string mode)
{
var sb = new StringBuilder();
@@ -192,48 +141,6 @@ namespace MediaBrowser.WebDashboard.Api
{
sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: file: filesystem: ws: wss:;\">");
}
- else
- {
- sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">");
- }
-
- sb.Append("<link rel=\"manifest\" href=\"manifest.json\">");
- sb.Append("<meta name=\"format-detection\" content=\"telephone=no\">");
- sb.Append("<meta name=\"msapplication-tap-highlight\" content=\"no\">");
-
- if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
- {
- sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no\">");
- }
- else
- {
- sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1\">");
- }
-
- sb.Append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
- sb.Append("<meta name=\"mobile-web-app-capable\" content=\"yes\">");
- sb.Append("<meta name=\"application-name\" content=\"Emby\">");
- //sb.Append("<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">");
-
- sb.Append("<meta name=\"robots\" content=\"noindex, nofollow, noarchive\">");
-
- // Open graph tags
- sb.Append("<meta property=\"og:title\" content=\"Emby\">");
- sb.Append("<meta property=\"og:site_name\" content=\"Emby\">");
- sb.Append("<meta property=\"og:url\" content=\"http://emby.media\">");
- sb.Append("<meta property=\"og:description\" content=\"Energize your media.\">");
- sb.Append("<meta property=\"og:type\" content=\"article\">");
- sb.Append("<meta property=\"fb:app_id\" content=\"1618309211750238\">");
-
- // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
- sb.Append("<link rel=\"apple-touch-icon\" href=\"touchicon.png\">");
- sb.Append("<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"touchicon72.png\">");
- sb.Append("<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"touchicon114.png\">");
- sb.Append("<link rel=\"apple-touch-startup-image\" href=\"css/images/iossplash.png\">");
- sb.Append("<link rel=\"shortcut icon\" href=\"css/images/favicon.ico\">");
- sb.Append("<meta name=\"msapplication-TileImage\" content=\"touchicon144.png\">");
- sb.Append("<meta name=\"msapplication-TileColor\" content=\"#333333\">");
- sb.Append("<meta name=\"theme-color\" content=\"#43A047\">");
return sb.ToString();
}
@@ -272,7 +179,7 @@ namespace MediaBrowser.WebDashboard.Api
files.Insert(0, "cordova.js");
}
- var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray(files.Count);
+ var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray();
builder.Append(string.Join(string.Empty, tags));
@@ -284,7 +191,7 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
private Stream GetRawResourceStream(string virtualPath)
{
- return _fileSystem.GetFileStream(GetResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+ return _resourceFileManager.GetResourceFileStream(_basePath, virtualPath);
}
}
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 02020531c..96d63be04 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -1,85 +1,23 @@
-<?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>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.WebDashboard</RootNamespace>
- <AssemblyName>MediaBrowser.WebDashboard</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProductVersion>10.0.0</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <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>
- <PropertyGroup>
- <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Api\ConfigurationPageInfo.cs" />
- <Compile Include="Api\DashboardService.cs" />
- <Compile Include="Api\PackageCreator.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ServerEntryPoint.cs" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</ItemGroup>
+
<ItemGroup>
<None Include="dashboard-ui\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup />
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+
<PropertyGroup>
- <PostBuildEvent>
- </PostBuildEvent>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
- <!-- 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
+
+</Project>
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.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.WebDashboard/ServerEntryPoint.cs b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
index b939e4107..9bf87ac0b 100644
--- a/MediaBrowser.WebDashboard/ServerEntryPoint.cs
+++ b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common;
+using System;
+using MediaBrowser.Common;
using MediaBrowser.Controller.Plugins;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
deleted file mode 100644
index 6b8deb9c9..000000000
--- a/MediaBrowser.WebDashboard/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.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index 9cc17481c..e4b11a6fc 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -8,7 +8,6 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Configuration;
using MediaBrowser.XbmcMetadata.Savers;
using System;
-using MediaBrowser.Controller.Dto;
namespace MediaBrowser.XbmcMetadata
{
@@ -32,48 +31,15 @@ namespace MediaBrowser.XbmcMetadata
public void Run()
{
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
- _libraryManager.ItemUpdated += _libraryManager_ItemUpdated;
- }
-
- void _libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e)
- {
- if (e.UpdateReason >= ItemUpdateType.ImageUpdate)
- {
- var person = e.Item as Person;
-
- if (person != null)
- {
- var config = _config.GetNfoConfiguration();
-
- if (!config.SaveImagePathsInNfo)
- {
- return;
- }
-
- var items = _libraryManager.GetItemList(new InternalItemsQuery
- {
- PersonIds = new[] { person.Id.ToString("N") },
- DtoOptions = new DtoOptions(true)
-
- });
-
- foreach (var item in items)
- {
- SaveMetadataForItem(item, e.UpdateReason);
- }
- }
- }
}
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
{
if (e.SaveReason == UserDataSaveReason.PlaybackFinished || e.SaveReason == UserDataSaveReason.TogglePlayed || e.SaveReason == UserDataSaveReason.UpdateUserRating)
{
- var item = e.Item as BaseItem;
-
if (!string.IsNullOrWhiteSpace(_config.GetNfoConfiguration().UserId))
{
- SaveMetadataForItem(item, ItemUpdateType.MetadataDownload);
+ SaveMetadataForItem(e.Item, ItemUpdateType.MetadataDownload);
}
}
}
@@ -81,14 +47,11 @@ namespace MediaBrowser.XbmcMetadata
public void Dispose()
{
_userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
- GC.SuppressFinalize(this);
}
private void SaveMetadataForItem(BaseItem item, ItemUpdateType updateReason)
{
- var locationType = item.LocationType;
- if (locationType == LocationType.Remote ||
- locationType == LocationType.Virtual)
+ if (!item.IsFileProtocol)
{
return;
}
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
index e84fa8e65..e4c9a0c35 100644
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
@@ -1,88 +1,17 @@
-<?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>{23499896-B135-4527-8574-C26E926EA99E}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.XbmcMetadata</RootNamespace>
- <AssemblyName>MediaBrowser.XbmcMetadata</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <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>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Configuration\NfoOptions.cs" />
- <Compile Include="EntryPoint.cs" />
- <Compile Include="Parsers\BaseNfoParser.cs" />
- <Compile Include="Parsers\EpisodeNfoParser.cs" />
- <Compile Include="Parsers\MovieNfoParser.cs" />
- <Compile Include="Parsers\SeasonNfoParser.cs" />
- <Compile Include="Parsers\SeriesNfoParser.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Providers\AlbumNfoProvider.cs" />
- <Compile Include="Providers\ArtistNfoProvider.cs" />
- <Compile Include="Providers\BaseNfoProvider.cs" />
- <Compile Include="Providers\BaseVideoNfoProvider.cs" />
- <Compile Include="Providers\EpisodeNfoProvider.cs" />
- <Compile Include="Providers\MovieNfoProvider.cs" />
- <Compile Include="Providers\SeasonNfoProvider.cs" />
- <Compile Include="Providers\SeriesNfoProvider.cs" />
- <Compile Include="Savers\AlbumNfoSaver.cs" />
- <Compile Include="Savers\ArtistNfoSaver.cs" />
- <Compile Include="Savers\BaseNfoSaver.cs" />
- <Compile Include="Savers\EpisodeNfoSaver.cs" />
- <Compile Include="Savers\MovieNfoSaver.cs" />
- <Compile Include="Savers\SeasonNfoSaver.cs" />
- <Compile Include="Savers\SeriesNfoSaver.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.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.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 689f175f3..3b8d68776 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -187,18 +187,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return;
}
- using (var ms = new MemoryStream())
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
{
- var bytes = Encoding.UTF8.GetBytes(xml);
-
- ms.Write(bytes, 0, bytes.Length);
- ms.Position = 0;
-
- // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
- try
+ using (var stringReader = new StringReader(xml))
{
// Use XmlReader for best performance
- using (var reader = XmlReader.Create(ms, settings))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
reader.MoveToContent();
reader.Read();
@@ -219,10 +214,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
}
- catch (XmlException)
- {
+ }
+ catch (XmlException)
+ {
- }
}
}
}
@@ -251,7 +246,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
var tmdbId = xml.Substring(index + srch.Length).TrimEnd('/').Split('-')[0];
int value;
- if (!string.IsNullOrWhiteSpace(tmdbId) && int.TryParse(tmdbId, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
+ if (!string.IsNullOrWhiteSpace(tmdbId) && int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{
item.SetProviderId(MetadataProviders.Tmdb, value.ToString(_usCulture));
}
@@ -267,7 +262,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
var tvdbId = xml.Substring(index + srch.Length).TrimEnd('/');
int value;
- if (!string.IsNullOrWhiteSpace(tvdbId) && int.TryParse(tvdbId, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
+ if (!string.IsNullOrWhiteSpace(tvdbId) && int.TryParse(tvdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{
item.SetProviderId(MetadataProviders.Tvdb, value.ToString(_usCulture));
}
@@ -380,18 +375,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
- case "website":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.HomePageUrl = val;
- }
-
- break;
- }
-
case "lockedfields":
{
var val = reader.ReadElementContentAsString();
@@ -592,15 +575,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
var val = reader.ReadElementContentAsString();
- var hasTrailer = item as IHasTrailers;
- if (hasTrailer != null)
+ if (!string.IsNullOrWhiteSpace(val))
{
- if (!string.IsNullOrWhiteSpace(val))
- {
- val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", "https://www.youtube.com/watch?v=", StringComparison.OrdinalIgnoreCase);
+ val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", BaseNfoSaver.YouTubeWatchUrl, StringComparison.OrdinalIgnoreCase);
- hasTrailer.AddTrailerUrl(val);
- }
+ item.AddTrailerUrl(val);
}
break;
}
@@ -977,7 +956,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
value = value.Trim().Trim(separator);
- return string.IsNullOrWhiteSpace(value) ? new string[] { } : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
+ return string.IsNullOrWhiteSpace(value) ? Array.Empty<string>() : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
@@ -987,7 +966,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <param name="separators">The separators.</param>
/// <param name="options">The options.</param>
/// <returns>System.String[][].</returns>
- private static string[] Split(string val, char[] separators, StringSplitOptions options)
+ private string[] Split(string val, char[] separators, StringSplitOptions options)
{
return val.Split(separators, options);
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index 953b59f46..ce7b924eb 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
Fetch(item, metadataFile, cancellationToken);
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
protected override void Fetch(MetadataResult<Episode> item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
{
@@ -44,18 +44,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
xml = xml.Substring(0, index + srch.Length);
}
- using (var ms = new MemoryStream())
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
{
- var bytes = Encoding.UTF8.GetBytes(xml);
-
- ms.Write(bytes, 0, bytes.Length);
- ms.Position = 0;
-
- // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
- try
+ using (var stringReader = new StringReader(xml))
{
// Use XmlReader for best performance
- using (var reader = XmlReader.Create(ms, settings))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
reader.MoveToContent();
reader.Read();
@@ -76,10 +71,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
}
- catch (XmlException)
- {
+ }
+ catch (XmlException)
+ {
- }
}
}
}
@@ -144,55 +139,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
- case "absolute_number":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- int rval;
-
- // int.TryParse is local aware, so it can be probamatic, force us culture
- if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
- {
- item.AbsoluteEpisodeNumber = rval;
- }
- }
-
- break;
- }
- case "DVD_episodenumber":
- {
- var number = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(number))
- {
- float num;
-
- if (float.TryParse(number, NumberStyles.Any, UsCulture, out num))
- {
- item.DvdEpisodeNumber = num;
- }
- }
- break;
- }
-
- case "DVD_season":
- {
- var number = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(number))
- {
- float num;
-
- if (float.TryParse(number, NumberStyles.Any, UsCulture, out num))
- {
- item.DvdSeasonNumber = Convert.ToInt32(num);
- }
- }
- break;
- }
-
case "airsbefore_episode":
{
var val = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 0283e4a7b..94b4c30f2 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -125,17 +125,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
private void ParseSetXml(string xml, Movie movie)
{
- using (var ms = new MemoryStream())
- {
- //xml = xml.Substring(xml.IndexOf('<'));
- //xml = xml.Substring(0, xml.LastIndexOf('>'));
- xml = "<set>" + xml + "</set>";
-
- var bytes = Encoding.UTF8.GetBytes(xml);
-
- ms.Write(bytes, 0, bytes.Length);
- ms.Position = 0;
+ //xml = xml.Substring(xml.IndexOf('<'));
+ //xml = xml.Substring(0, xml.LastIndexOf('>'));
+ using (var stringReader = new StringReader("<set>" + xml + "</set>"))
+ {
// These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
try
{
@@ -146,7 +140,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
settings.IgnoreComments = true;
// Use XmlReader for best performance
- using (var reader = XmlReader.Create(ms, settings))
+ using (var reader = XmlReader.Create(stringReader, settings))
{
reader.MoveToContent();
reader.Read();
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
index 4803004dd..123c0493f 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.XbmcMetadata.Providers
{
public abstract class BaseNfoProvider<T> : ILocalMetadataProvider<T>, IHasItemChangeMonitor
- where T : IHasMetadata, new()
+ where T : BaseItem, new()
{
protected IFileSystem FileSystem;
@@ -56,7 +56,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
var file = GetXmlFile(new ItemInfo(item), directoryService);
diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
index f9f9c9b98..dcce00d69 100644
--- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
@@ -18,17 +18,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class AlbumNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "album.nfo");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "album";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -38,7 +38,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return item is MusicAlbum && updateType >= MinimumUpdateType;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var album = (MusicAlbum)item;
@@ -55,7 +55,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
AddTracks(album.Tracks, writer);
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
private void AddTracks(IEnumerable<BaseItem> tracks, XmlWriter writer)
{
@@ -84,7 +84,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
index a7f4e56a6..f571dc2b7 100644
--- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
@@ -17,17 +17,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class ArtistNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "artist.nfo");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "artist";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -37,7 +37,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return item is MusicArtist && updateType >= MinimumUpdateType;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var artist = (MusicArtist)item;
@@ -54,7 +54,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
AddAlbums(albums, writer);
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
private void AddAlbums(IList<BaseItem> albums, XmlWriter writer)
{
@@ -76,7 +76,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index d32081458..505fec65f 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -28,6 +28,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public abstract class BaseNfoSaver : IMetadataFileSaver
{
+ public static readonly string YouTubeWatchUrl = "https://www.youtube.com/watch?v=";
+
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private static readonly Dictionary<string, string> CommonTags = new[] {
@@ -42,7 +44,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
"sorttitle",
"mpaa",
"aspectratio",
- "website",
"collectionnumber",
"tmdbid",
"rottentomatoesid",
@@ -147,7 +148,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- public string GetSavePath(IHasMetadata item)
+ public string GetSavePath(BaseItem item)
{
return GetLocalSavePath(item);
}
@@ -157,14 +158,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- protected abstract string GetLocalSavePath(IHasMetadata item);
+ protected abstract string GetLocalSavePath(BaseItem item);
/// <summary>
/// Gets the name of the root element.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- protected abstract string GetRootElementName(IHasMetadata item);
+ protected abstract string GetRootElementName(BaseItem item);
/// <summary>
/// Determines whether [is enabled for] [the specified item].
@@ -172,9 +173,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// <param name="item">The item.</param>
/// <param name="updateType">Type of the update.</param>
/// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
- public abstract bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
+ public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType);
- protected virtual List<string> GetTagsUsed(IHasMetadata item)
+ protected virtual List<string> GetTagsUsed(BaseItem item)
{
var list = new List<string>();
foreach (var providerKey in item.ProviderIds.Keys)
@@ -188,7 +189,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ public void Save(BaseItem item, CancellationToken cancellationToken)
{
var path = GetSavePath(item);
@@ -207,27 +208,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
private void SaveToFile(Stream stream, string path)
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
-
- var file = FileSystem.GetFileInfo(path);
-
- var wasHidden = false;
-
- // This will fail if the file is hidden
- if (file.Exists)
- {
- if (file.IsHidden)
- {
- wasHidden = true;
- }
- FileSystem.SetAttributes(path, false, false);
- }
+ // On Windows, savint the file will fail if the file is hidden or readonly
+ FileSystem.SetAttributes(path, false, false);
using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(filestream);
}
- if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden)
+ if (ConfigurationManager.Configuration.SaveMetadataHidden)
{
SetHidden(path, true);
}
@@ -245,7 +234,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private void Save(IHasMetadata item, Stream stream, string xmlPath)
+ private void Save(BaseItem item, Stream stream, string xmlPath)
{
var settings = new XmlWriterSettings
{
@@ -262,7 +251,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteStartElement(root);
- var baseItem = item as BaseItem;
+ var baseItem = item;
if (baseItem != null)
{
@@ -303,7 +292,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected abstract void WriteCustomElements(IHasMetadata item, XmlWriter writer);
+ protected abstract void WriteCustomElements(BaseItem item, XmlWriter writer);
public static void AddMediaInfo<T>(T item, XmlWriter writer)
where T : IHasMediaSources
@@ -389,13 +378,13 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (stream.Type == MediaStreamType.Video)
{
- var runtimeTicks = ((IHasMetadata) item).RunTimeTicks;
+ var runtimeTicks = item.RunTimeTicks;
if (runtimeTicks.HasValue)
{
var timespan = TimeSpan.FromTicks(runtimeTicks.Value);
- writer.WriteElementString("duration", Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture));
- writer.WriteElementString("durationinseconds", Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture));
+ writer.WriteElementString("duration", Math.Floor(timespan.TotalMinutes).ToString(UsCulture));
+ writer.WriteElementString("durationinseconds", Math.Floor(timespan.TotalSeconds).ToString(UsCulture));
}
var video = item as Video;
@@ -454,7 +443,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// Adds the common nodes.
/// </summary>
/// <returns>Task.</returns>
- public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
+ private void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
{
var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
@@ -539,13 +528,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("credits", person);
}
- var hasTrailer = item as IHasTrailers;
- if (hasTrailer != null)
+ foreach (var trailer in item.RemoteTrailers)
{
- foreach (var trailer in hasTrailer.RemoteTrailers)
- {
- writer.WriteElementString("trailer", GetOutputTrailerUrl(trailer.Url));
- }
+ writer.WriteElementString("trailer", GetOutputTrailerUrl(trailer.Url));
}
if (item.CommunityRating.HasValue)
@@ -578,11 +563,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- if (!string.IsNullOrEmpty(item.HomePageUrl))
- {
- writer.WriteElementString("website", item.HomePageUrl);
- }
-
var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
if (!string.IsNullOrEmpty(tmdbCollection))
@@ -679,7 +659,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
- writer.WriteElementString("runtime", Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture));
+ writer.WriteElementString("runtime", Convert.ToInt64(timespan.TotalMinutes).ToString(UsCulture));
}
if (!string.IsNullOrWhiteSpace(item.Tagline))
@@ -791,8 +771,23 @@ namespace MediaBrowser.XbmcMetadata.Savers
var providerId = item.ProviderIds[providerKey];
if (!string.IsNullOrEmpty(providerId) && !writtenProviderIds.Contains(providerKey))
{
- writer.WriteElementString(GetTagForProviderKey(providerKey), providerId);
- writtenProviderIds.Add(providerKey);
+ try
+ {
+ var tagName = GetTagForProviderKey(providerKey);
+ //Logger.Debug("Verifying custom provider tagname {0}", tagName);
+ XmlConvert.VerifyName(tagName);
+ //Logger.Debug("Saving custom provider tagname {0}", tagName);
+
+ writer.WriteElementString(GetTagForProviderKey(providerKey), providerId);
+ }
+ catch (ArgumentException)
+ {
+ // catch invalid names without failing the entire operation
+ }
+ catch (XmlException)
+ {
+ // catch invalid names without failing the entire operation
+ }
}
}
}
@@ -813,24 +808,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- public static void AddChapters(Video item, XmlWriter writer, IItemRepository repository)
- {
- var chapters = repository.GetChapters(item.Id);
-
- foreach (var chapter in chapters)
- {
- writer.WriteStartElement("chapter");
- writer.WriteElementString("name", chapter.Name);
-
- var time = TimeSpan.FromTicks(chapter.StartPositionTicks);
- var ms = Convert.ToInt64(time.TotalMilliseconds);
-
- writer.WriteElementString("startpositionms", ms.ToString(UsCulture));
- writer.WriteEndElement();
- }
- }
-
- private static void AddCollectionItems(Folder item, XmlWriter writer)
+ private void AddCollectionItems(Folder item, XmlWriter writer)
{
var items = item.LinkedChildren
.Where(i => i.Type == LinkedChildType.Manual)
@@ -845,6 +823,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("path", link.Path);
}
+ if (!string.IsNullOrWhiteSpace(link.LibraryItemId))
+ {
+ writer.WriteElementString("ItemId", link.LibraryItemId);
+ }
+
writer.WriteEndElement();
}
}
@@ -854,16 +837,13 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>System.String.</returns>
- private static string GetOutputTrailerUrl(string url)
+ private string GetOutputTrailerUrl(string url)
{
// This is what xbmc expects
-
- return url.Replace("https://www.youtube.com/watch?v=",
- "plugin://plugin.video.youtube/?action=play_video&videoid=",
- StringComparison.OrdinalIgnoreCase);
+ return url.Replace(YouTubeWatchUrl, "plugin://plugin.video.youtube/?action=play_video&videoid=", StringComparison.OrdinalIgnoreCase);
}
- private static void AddImages(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IServerConfigurationManager config)
+ private void AddImages(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IServerConfigurationManager config)
{
writer.WriteStartElement("art");
@@ -882,7 +862,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement();
}
- private static void AddUserData(BaseItem item, XmlWriter writer, IUserManager userManager, IUserDataManager userDataRepo, XbmcMetadataOptions options)
+ private void AddUserData(BaseItem item, XmlWriter writer, IUserManager userManager, IUserDataManager userDataRepo, XbmcMetadataOptions options)
{
var userId = options.UserId;
if (string.IsNullOrWhiteSpace(userId))
@@ -932,7 +912,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement();
}
- private static void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config, bool saveImagePath)
+ private void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config, bool saveImagePath)
{
var actors = people
.Where(i => !IsPersonType(i, PersonType.Director) && !IsPersonType(i, PersonType.Writer))
@@ -984,7 +964,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private static string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager, IServerConfigurationManager config)
+ private string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager, IServerConfigurationManager config)
{
if (!image.IsLocalFile)
{
@@ -994,7 +974,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return libraryManager.GetPathAfterNetworkSubstitution(image.Path);
}
- private static bool IsPersonType(PersonInfo person, string type)
+ private bool IsPersonType(PersonInfo person, string type)
{
return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
}
@@ -1052,7 +1032,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private static string GetTagForProviderKey(string providerKey)
+ private string GetTagForProviderKey(string providerKey)
{
return providerKey.ToLower() + "id";
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
index bbd3a8d34..33f96fc8f 100644
--- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
@@ -17,17 +17,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class EpisodeNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.ChangeExtension(item.Path, ".nfo");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "episodedetails";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -37,7 +37,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return item is Episode && updateType >= MinimumUpdateType;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var episode = (Episode)item;
@@ -89,26 +89,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("displayseason", specialSeason.Value.ToString(UsCulture));
}
}
-
- if (episode.DvdEpisodeNumber.HasValue)
- {
- writer.WriteElementString("DVD_episodenumber", episode.DvdEpisodeNumber.Value.ToString(UsCulture));
- }
-
- if (episode.DvdSeasonNumber.HasValue)
- {
- writer.WriteElementString("DVD_season", episode.DvdSeasonNumber.Value.ToString(UsCulture));
- }
-
- if (episode.AbsoluteEpisodeNumber.HasValue)
- {
- writer.WriteElementString("absolute_number", episode.AbsoluteEpisodeNumber.Value.ToString(UsCulture));
- }
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
@@ -120,9 +105,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
"airsafter_season",
"airsbefore_episode",
"airsbefore_season",
- "DVD_episodenumber",
- "DVD_season",
- "absolute_number",
"displayseason",
"displayepisode"
});
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index 1989a2c28..0fc74e66b 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class MovieNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
var paths = GetMovieSavePaths(new ItemInfo(item), FileSystem);
return paths.Count == 0 ? null : paths[0];
@@ -61,12 +61,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return item is MusicVideo ? "musicvideo" : "movie";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -76,7 +76,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
var video = item as Video;
// Check parent for null to avoid running this against things like video backdrops
- if (video != null && !(item is Episode) && !video.IsOwnedItem)
+ if (video != null && !(item is Episode) && !video.ExtraType.HasValue)
{
return updateType >= MinimumUpdateType;
}
@@ -84,7 +84,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return false;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var imdb = item.GetProviderId(MetadataProviders.Imdb);
@@ -118,7 +118,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
index 85ed307c1..5a626eef9 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
@@ -16,17 +16,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeasonNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "season.nfo");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "season";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -41,7 +41,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && FileSystem.FileExists(GetSavePath(item)));
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var season = (Season)item;
@@ -51,7 +51,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index a2f07d952..27bc5f038 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -17,17 +17,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeriesNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(BaseItem item)
{
return Path.Combine(item.Path, "tvshow.nfo");
}
- protected override string GetRootElementName(IHasMetadata item)
+ protected override string GetRootElementName(BaseItem item)
{
return "tvshow";
}
- public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -37,7 +37,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return item is Series && updateType >= MinimumUpdateType;
}
- protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var series = (Series)item;
@@ -71,7 +71,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- protected override List<string> GetTagsUsed(IHasMetadata item)
+ protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
list.AddRange(new string[]
@@ -80,7 +80,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
"episodeguide",
"season",
"episode",
- "status"
+ "status",
+ "displayorder"
});
return list;
}
diff --git a/MediaBrowser.XbmcMetadata/packages.config b/MediaBrowser.XbmcMetadata/packages.config
deleted file mode 100644
index 6b8deb9c9..000000000
--- a/MediaBrowser.XbmcMetadata/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.sln b/MediaBrowser.sln
index 32485a8c9..b29bde97d 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -3,72 +3,58 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C5D6ABC-D277-407B-8061-3AA04251D539}"
- ProjectSection(SolutionItems) = preProject
- Performance1.psess = Performance1.psess
- Performance19.psess = Performance19.psess
- Performance2.psess = Performance2.psess
- Performance3.psess = Performance3.psess
- Performance4.psess = Performance4.psess
- Performance5.psess = Performance5.psess
- Performance6.psess = Performance6.psess
- Performance7.psess = Performance7.psess
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget (2)", ".nuget (2)", "{E60FB157-87E2-4A41-8B04-27EA49B63B4D}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{E60FB157-87E2-4A41-8B04-27EA49B63B4D}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Tests", "MediaBrowser.Tests\MediaBrowser.Tests.csproj", "{E22BFD35-0FCD-4A85-978A-C22DCD73A081}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplication", "MediaBrowser.ServerApplication\MediaBrowser.ServerApplication.csproj", "{94ADE4D3-B7EC-45CD-A200-CC469433072B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmbyServer", "MediaBrowser.Server.Mono\EmbyServer.csproj", "{175A9388-F352-4586-A6B4-070DED62B644}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{175A9388-F352-4586-A6B4-070DED62B644}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Drawing.Skia", "Emby.Drawing.Skia\Emby.Drawing.Skia.csproj", "{2312DA6D-FF86-4597-9777-BCEEC32D96DD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Skia", "Emby.Drawing.Skia\Emby.Drawing.Skia.csproj", "{2312DA6D-FF86-4597-9777-BCEEC32D96DD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SocketHttpListener", "SocketHttpListener\SocketHttpListener.csproj", "{1D74413B-E7CF-455B-B021-F52BDF881542}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Notifications", "Emby.Notifications\Emby.Notifications.csproj", "{2E030C33-6923-4530-9E54-FA29FA6AD1A9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener", "SocketHttpListener\SocketHttpListener.csproj", "{1D74413B-E7CF-455B-B021-F52BDF881542}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -77,21 +63,11 @@ Global
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
- Release Mono|Any CPU = Release Mono|Any CPU
- Release Mono|Mixed Platforms = Release Mono|Mixed Platforms
- Release Mono|Win32 = Release Mono|Win32
- Release Mono|x64 = Release Mono|x64
- Release Mono|x86 = Release Mono|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
Release|x64 = Release|x64
Release|x86 = Release|x86
- Signed|Any CPU = Signed|Any CPU
- Signed|Mixed Platforms = Signed|Mixed Platforms
- Signed|Win32 = Signed|Win32
- Signed|x64 = Signed|x64
- Signed|x86 = Signed|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -102,14 +78,6 @@ Global
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x64.ActiveCfg = Debug|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.ActiveCfg = Debug|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.Build.0 = Debug|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.Build.0 = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.Build.0 = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -118,16 +86,6 @@ Global
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x64.ActiveCfg = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.ActiveCfg = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Any CPU.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Win32.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Win32.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x64.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x64.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.Build.0 = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -136,14 +94,6 @@ Global
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x64.ActiveCfg = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.ActiveCfg = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.Build.0 = Debug|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|x86.Build.0 = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.Build.0 = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -152,16 +102,6 @@ Global
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x64.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Win32.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Win32.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x64.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x64.Build.0 = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.ActiveCfg = Release|Any CPU
- {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.Build.0 = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -170,14 +110,6 @@ Global
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x64.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.Build.0 = Debug|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|x86.Build.0 = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.Build.0 = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -186,16 +118,6 @@ Global
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x64.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Any CPU.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Win32.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Win32.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x64.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x64.Build.0 = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.ActiveCfg = Release|Any CPU
- {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.Build.0 = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -204,14 +126,6 @@ Global
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x64.ActiveCfg = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.ActiveCfg = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.Build.0 = Debug|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.Build.0 = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.Build.0 = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -220,16 +134,6 @@ Global
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x64.ActiveCfg = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.ActiveCfg = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Any CPU.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Win32.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Win32.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x64.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x64.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x86.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x86.Build.0 = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -238,14 +142,6 @@ Global
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x64.ActiveCfg = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.ActiveCfg = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.Build.0 = Debug|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.Build.0 = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|Any CPU.Build.0 = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -254,46 +150,6 @@ Global
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x64.ActiveCfg = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.ActiveCfg = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Win32.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Win32.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x64.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x64.Build.0 = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|x64.ActiveCfg = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Debug|x86.ActiveCfg = Debug|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|Any CPU.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|Win32.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|x64.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Release|x86.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Any CPU.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Win32.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|Win32.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|x64.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|x64.Build.0 = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|x86.ActiveCfg = Release|Any CPU
- {E22BFD35-0FCD-4A85-978A-C22DCD73A081}.Signed|x86.Build.0 = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -301,13 +157,6 @@ Global
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Win32.ActiveCfg = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x64.ActiveCfg = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x86.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|Any CPU.Build.0 = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -315,46 +164,6 @@ Global
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|Win32.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x64.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Win32.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Win32.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x64.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x64.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.Build.0 = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|Any CPU.Build.0 = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|Win32.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|x64.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Release|x86.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Any CPU.Build.0 = Release|Any CPU
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Mixed Platforms.ActiveCfg = Debug|x86
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Mixed Platforms.Build.0 = Debug|x86
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Win32.ActiveCfg = Debug|x86
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|Win32.Build.0 = Debug|x86
- {94ADE4D3-B7EC-45CD-A200-CC469433072B}.Signed|x64.ActiveCfg = Release|Any CPU
- {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
{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
@@ -362,13 +171,6 @@ Global
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Win32.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x64.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x86.ActiveCfg = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Any CPU.Build.0 = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -376,16 +178,6 @@ Global
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Win32.ActiveCfg = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x64.ActiveCfg = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Win32.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Win32.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x64.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x64.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.Build.0 = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -393,13 +185,6 @@ Global
{23499896-B135-4527-8574-C26E926EA99E}.Debug|Win32.ActiveCfg = Debug|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|x64.ActiveCfg = Debug|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x86.ActiveCfg = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.Build.0 = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -407,16 +192,6 @@ Global
{23499896-B135-4527-8574-C26E926EA99E}.Release|Win32.ActiveCfg = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|x64.ActiveCfg = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|x86.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Any CPU.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Win32.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|Win32.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|x64.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|x64.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|x86.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Signed|x86.Build.0 = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -424,13 +199,6 @@ Global
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Win32.ActiveCfg = Debug|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x64.ActiveCfg = Debug|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x86.ActiveCfg = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.Build.0 = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -438,16 +206,6 @@ Global
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Win32.ActiveCfg = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x64.ActiveCfg = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Any CPU.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Win32.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Win32.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x64.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x64.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x86.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x86.Build.0 = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -457,15 +215,6 @@ Global
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x64.ActiveCfg = Debug|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.ActiveCfg = Debug|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.Build.0 = Debug|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|Win32.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Release Mono|x86.Build.0 = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|Mixed Platforms.Build.0 = Release|Any CPU
@@ -474,16 +223,6 @@ Global
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x64.ActiveCfg = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.ActiveCfg = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Any CPU.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Win32.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Win32.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x64.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x64.Build.0 = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x86.ActiveCfg = Release|Any CPU
- {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x86.Build.0 = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -491,13 +230,6 @@ Global
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Win32.ActiveCfg = Debug|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|x64.ActiveCfg = Debug|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|x86.ActiveCfg = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x86.ActiveCfg = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Any CPU.Build.0 = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -505,16 +237,6 @@ Global
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Win32.ActiveCfg = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x64.ActiveCfg = Release|Any CPU
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x86.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Any CPU.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Win32.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Win32.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x64.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x64.Build.0 = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x86.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x86.Build.0 = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -525,16 +247,6 @@ Global
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|x64.Build.0 = Debug|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|x86.ActiveCfg = Debug|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|x86.Build.0 = Debug|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Win32.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|x64.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|x86.Build.0 = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -545,16 +257,6 @@ Global
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|x64.Build.0 = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|x86.ActiveCfg = Release|Any CPU
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|x86.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Any CPU.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Win32.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|Win32.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|x64.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|x64.Build.0 = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|x86.ActiveCfg = Release|Any CPU
- {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|x86.Build.0 = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -565,16 +267,6 @@ Global
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|x64.Build.0 = Debug|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|x86.ActiveCfg = Debug|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|x86.Build.0 = Debug|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|Win32.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|x64.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release Mono|x86.Build.0 = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.Build.0 = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -585,16 +277,6 @@ Global
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x64.Build.0 = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x86.ActiveCfg = Release|Any CPU
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x86.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Any CPU.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Win32.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|Win32.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|x64.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|x64.Build.0 = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|x86.ActiveCfg = Release|Any CPU
- {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|x86.Build.0 = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -605,16 +287,6 @@ Global
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|x64.Build.0 = Debug|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|x86.ActiveCfg = Debug|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|x86.Build.0 = Debug|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Win32.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|x64.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|x86.Build.0 = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.Build.0 = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -625,16 +297,6 @@ Global
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x64.Build.0 = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.ActiveCfg = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Any CPU.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Win32.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|Win32.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|x64.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|x64.Build.0 = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|x86.ActiveCfg = Release|Any CPU
- {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|x86.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -645,16 +307,6 @@ Global
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|x64.Build.0 = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|x86.ActiveCfg = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|x86.Build.0 = Debug|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|Win32.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|x64.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Release Mono|x86.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -665,16 +317,6 @@ Global
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x64.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.ActiveCfg = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Any CPU.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Win32.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|Win32.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|x64.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|x64.Build.0 = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|x86.ActiveCfg = Release|Any CPU
- {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|x86.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -685,16 +327,6 @@ Global
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x64.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.Build.0 = Debug|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Win32.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x64.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -705,16 +337,6 @@ Global
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x64.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Any CPU.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Win32.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|Win32.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|x64.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|x64.Build.0 = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|x86.ActiveCfg = Release|Any CPU
- {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|x86.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -725,16 +347,6 @@ Global
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x64.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x86.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x86.Build.0 = Debug|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Win32.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x64.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -745,16 +357,6 @@ Global
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x64.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Any CPU.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Win32.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|Win32.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x64.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x64.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x86.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x86.Build.0 = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -765,16 +367,6 @@ Global
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x64.Build.0 = Debug|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x86.Build.0 = Debug|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|Win32.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x64.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release Mono|x86.Build.0 = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Any CPU.Build.0 = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -785,16 +377,6 @@ Global
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x64.Build.0 = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x86.ActiveCfg = Release|Any CPU
{6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x86.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Any CPU.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Win32.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|Win32.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x64.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x64.Build.0 = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.ActiveCfg = Release|Any CPU
- {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.Build.0 = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -805,16 +387,6 @@ Global
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x64.Build.0 = Debug|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x86.ActiveCfg = Debug|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x86.Build.0 = Debug|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Win32.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x64.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x86.Build.0 = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Any CPU.Build.0 = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -825,16 +397,6 @@ Global
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x64.Build.0 = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x86.ActiveCfg = Release|Any CPU
{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x86.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Any CPU.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Win32.ActiveCfg = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Win32.Build.0 = Release|Any CPU
- {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x64.ActiveCfg = Release|Any CPU
- {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
{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
@@ -845,16 +407,6 @@ Global
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|x64.Build.0 = Debug|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|x86.ActiveCfg = Debug|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|x86.Build.0 = Debug|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|Win32.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|x64.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release Mono|x86.Build.0 = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.Build.0 = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -865,16 +417,6 @@ Global
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|x64.Build.0 = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|x86.ActiveCfg = Release|Any CPU
{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|x86.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Any CPU.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Win32.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|Win32.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|x64.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|x64.Build.0 = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|x86.ActiveCfg = Release|Any CPU
- {CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Signed|x86.Build.0 = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -885,16 +427,6 @@ Global
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|x64.Build.0 = Debug|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|x86.ActiveCfg = Debug|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|x86.Build.0 = Debug|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|Win32.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|x64.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Release Mono|x86.Build.0 = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|Any CPU.Build.0 = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
@@ -905,16 +437,46 @@ Global
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|x64.Build.0 = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|x86.ActiveCfg = Release|Any CPU
{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|x86.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Any CPU.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Any CPU.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Win32.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|Win32.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|x64.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|x64.Build.0 = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|x86.ActiveCfg = Release|Any CPU
- {1D74413B-E7CF-455B-B021-F52BDF881542}.Signed|x86.Build.0 = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Win32.Build.0 = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|x64.Build.0 = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|x86.Build.0 = Debug|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Win32.ActiveCfg = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Win32.Build.0 = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|x64.ActiveCfg = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|x64.Build.0 = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|x86.ActiveCfg = Release|Any CPU
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|x86.Build.0 = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Win32.Build.0 = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|x64.Build.0 = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|x86.Build.0 = Debug|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Win32.ActiveCfg = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Win32.Build.0 = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|x64.ActiveCfg = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|x64.Build.0 = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Mono.Nat/AsyncResults/AsyncResult.cs b/Mono.Nat/AsyncResults/AsyncResult.cs
deleted file mode 100644
index e98e7d7ca..000000000
--- a/Mono.Nat/AsyncResults/AsyncResult.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-
-namespace Mono.Nat
-{
- internal class AsyncResult : IAsyncResult
- {
- private object asyncState;
- private AsyncCallback callback;
- private bool completedSynchronously;
- private bool isCompleted;
- private Exception storedException;
- private ManualResetEvent waitHandle;
-
- public AsyncResult(AsyncCallback callback, object asyncState)
- {
- this.callback = callback;
- this.asyncState = asyncState;
- waitHandle = new ManualResetEvent(false);
- }
-
- public object AsyncState
- {
- get { return asyncState; }
- }
-
- public ManualResetEvent AsyncWaitHandle
- {
- get { return waitHandle; }
- }
-
- WaitHandle IAsyncResult.AsyncWaitHandle
- {
- get { return waitHandle; }
- }
-
- public bool CompletedSynchronously
- {
- get { return completedSynchronously; }
- protected internal set { completedSynchronously = value; }
- }
-
- public bool IsCompleted
- {
- get { return isCompleted; }
- protected internal set { isCompleted = value; }
- }
-
- public Exception StoredException
- {
- get { return storedException; }
- }
-
- public void Complete()
- {
- Complete(storedException);
- }
-
- public void Complete(Exception ex)
- {
- storedException = ex;
- isCompleted = true;
- waitHandle.Set();
-
- if (callback != null)
- callback(this);
- }
- }
-}
diff --git a/Mono.Nat/Enums/MapState.cs b/Mono.Nat/Enums/MapState.cs
deleted file mode 100644
index 5ed2abd8f..000000000
--- a/Mono.Nat/Enums/MapState.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// Authors:
-// Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat
-{
- public enum MapState
- {
- AlreadyMapped,
- Available
- }
-} \ No newline at end of file
diff --git a/Mono.Nat/Exceptions/MappingException.cs b/Mono.Nat/Exceptions/MappingException.cs
deleted file mode 100644
index 9c0c4f122..000000000
--- a/Mono.Nat/Exceptions/MappingException.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Authors:
-// Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat
-{
- public class MappingException : Exception
- {
- private int errorCode;
- private string errorText;
-
- public int ErrorCode
- {
- get { return this.errorCode; }
- }
-
- public string ErrorText
- {
- get { return this.errorText; }
- }
-
- public MappingException()
- : base()
- {
- }
-
- public MappingException(string message)
- : base(message)
- {
- }
-
- public MappingException(int errorCode, string errorText)
- : base (string.Format ("Error {0}: {1}", errorCode, errorText))
- {
- this.errorCode = errorCode;
- this.errorText = errorText;
- }
-
- public MappingException(string message, Exception innerException)
- : base(message, innerException)
- {
- }
- }
-}
diff --git a/Mono.Nat/ISearcher.cs b/Mono.Nat/ISearcher.cs
index 51042bf27..fff7e8d75 100644
--- a/Mono.Nat/ISearcher.cs
+++ b/Mono.Nat/ISearcher.cs
@@ -33,20 +33,14 @@ using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
-using System.Threading.Tasks;
namespace Mono.Nat
{
- public delegate void NatDeviceCallback(INatDevice device);
-
internal interface ISearcher
{
event EventHandler<DeviceEventArgs> DeviceFound;
- event EventHandler<DeviceEventArgs> DeviceLost;
void Search();
void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint);
- DateTime NextSearch { get; }
- NatProtocol Protocol { get; }
}
}
diff --git a/Mono.Nat/Mono.Nat.csproj b/Mono.Nat/Mono.Nat.csproj
index 426824f0d..4de89290e 100644
--- a/Mono.Nat/Mono.Nat.csproj
+++ b/Mono.Nat/Mono.Nat.csproj
@@ -1,97 +1,17 @@
-<?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>{CB7F2326-6497-4A3D-BA03-48513B17A7BE}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Mono.Nat</RootNamespace>
- <AssemblyName>Mono.Nat</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>
- </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>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <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="AbstractNatDevice.cs" />
- <Compile Include="AsyncResults\AsyncResult.cs" />
- <Compile Include="Enums\MapState.cs" />
- <Compile Include="Enums\ProtocolType.cs" />
- <Compile Include="EventArgs\DeviceEventArgs.cs" />
- <Compile Include="Exceptions\MappingException.cs" />
- <Compile Include="INatDevice.cs" />
- <Compile Include="ISearcher.cs" />
- <Compile Include="Mapping.cs" />
- <Compile Include="NatProtocol.cs" />
- <Compile Include="NatUtility.cs" />
- <Compile Include="Pmp\AsyncResults\PortMapAsyncResult.cs" />
- <Compile Include="Pmp\PmpConstants.cs" />
- <Compile Include="Pmp\PmpNatDevice.cs" />
- <Compile Include="Pmp\Searchers\PmpSearcher.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Upnp\Messages\DiscoverDeviceMessage.cs" />
- <Compile Include="Upnp\Messages\ErrorMessage.cs" />
- <Compile Include="Upnp\Messages\GetServicesMessage.cs" />
- <Compile Include="Upnp\Messages\Requests\CreatePortMappingMessage.cs" />
- <Compile Include="Upnp\Messages\Responses\CreatePortMappingResponseMessage.cs" />
- <Compile Include="Upnp\Messages\UpnpMessage.cs" />
- <Compile Include="Upnp\Searchers\UpnpSearcher.cs" />
- <Compile Include="Upnp\UpnpNatDevice.cs" />
- </ItemGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Folder Include="Upnp\AsyncResults\" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
+
<ItemGroup>
- <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>
+ <Compile Include="..\SharedVersion.cs"/>
</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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/Mono.Nat/NatManager.cs b/Mono.Nat/NatManager.cs
new file mode 100644
index 000000000..752fd0416
--- /dev/null
+++ b/Mono.Nat/NatManager.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Net;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Logging;
+using System.Linq;
+
+namespace Mono.Nat
+{
+ public class NatManager : IDisposable
+ {
+ public event EventHandler<DeviceEventArgs> DeviceFound;
+
+ private List<ISearcher> controllers = new List<ISearcher>();
+
+ private ILogger Logger;
+ private IHttpClient HttpClient;
+
+ public NatManager(ILogger logger, IHttpClient httpClient)
+ {
+ Logger = logger;
+ HttpClient = httpClient;
+ }
+
+ private object _runSyncLock = new object();
+ public void StartDiscovery()
+ {
+ lock (_runSyncLock)
+ {
+ if (controllers.Count > 0)
+ {
+ return;
+ }
+
+ controllers.Add(new PmpSearcher(Logger));
+
+ foreach (var searcher in controllers)
+ {
+ searcher.DeviceFound += Searcher_DeviceFound;
+ }
+ }
+ }
+
+ public void StopDiscovery()
+ {
+ lock (_runSyncLock)
+ {
+ var disposables = controllers.OfType<IDisposable>().ToList();
+ controllers.Clear();
+
+ foreach (var disposable in disposables)
+ {
+ disposable.Dispose();
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ StopDiscovery();
+ }
+
+ public Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol)
+ {
+ switch (protocol)
+ {
+ case NatProtocol.Upnp:
+ var searcher = new UpnpSearcher(Logger, HttpClient);
+ searcher.DeviceFound += Searcher_DeviceFound;
+ return searcher.Handle(localAddress, deviceInfo, endpoint);
+ default:
+ throw new ArgumentException("Unexpected protocol: " + protocol);
+ }
+ }
+
+ private void Searcher_DeviceFound(object sender, DeviceEventArgs e)
+ {
+ if (DeviceFound != null)
+ {
+ DeviceFound(sender, e);
+ }
+ }
+ }
+}
diff --git a/Mono.Nat/NatUtility.cs b/Mono.Nat/NatUtility.cs
deleted file mode 100644
index bf703e399..000000000
--- a/Mono.Nat/NatUtility.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-//
-// Authors:
-// Ben Motmans <ben.motmans@gmail.com>
-// Nicholas Terry <nick.i.terry@gmail.com>
-//
-// Copyright (C) 2007 Ben Motmans
-// Copyright (C) 2014 Nicholas Terry
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading;
-using System.Collections.Generic;
-using System.IO;
-using System.Net.NetworkInformation;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Logging;
-
-namespace Mono.Nat
-{
- public static class NatUtility
- {
- public static event EventHandler<DeviceEventArgs> DeviceFound;
- public static event EventHandler<DeviceEventArgs> DeviceLost;
-
- private static List<ISearcher> controllers;
- private static bool verbose;
-
- public static List<NatProtocol> EnabledProtocols { get; set; }
-
- public static ILogger Logger { get; set; }
- public static IHttpClient HttpClient { get; set; }
-
- public static bool Verbose
- {
- get { return verbose; }
- set { verbose = value; }
- }
-
- static NatUtility()
- {
- EnabledProtocols = new List<NatProtocol>
- {
- NatProtocol.Pmp
- };
-
- controllers = new List<ISearcher>();
- controllers.Add(PmpSearcher.Instance);
-
- controllers.ForEach(searcher =>
- {
- searcher.DeviceFound += (sender, args) =>
- {
- if (DeviceFound != null)
- DeviceFound(sender, args);
- };
- searcher.DeviceLost += (sender, args) =>
- {
- if (DeviceLost != null)
- DeviceLost(sender, args);
- };
- });
- }
-
- internal static void Log(string format, params object[] args)
- {
- var logger = Logger;
- if (logger != null)
- logger.Debug(format, args);
- }
-
- private static async Task SearchAndListen(CancellationToken cancellationToken)
- {
- while (!cancellationToken.IsCancellationRequested)
- {
- try
- {
- 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))
- {
- Log("Searching for: {0}", s.GetType().Name);
- s.Search();
- }
- }
- }
- catch (Exception e)
- {
-
- }
- await Task.Delay(100).ConfigureAwait(false);
- }
- }
-
- static async Task Receive(ISearcher searcher, List<UdpClient> clients)
- {
- foreach (UdpClient client in clients)
- {
- if (client.Available > 0)
- {
- IPAddress localAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address;
- var result = await client.ReceiveAsync().ConfigureAwait(false);
- var data = result.Buffer;
- var received = result.RemoteEndPoint;
- searcher.Handle(localAddress, data, received);
- }
- }
- }
-
- private static CancellationTokenSource _currentCancellationTokenSource;
- private static object _runSyncLock = new object();
- public static void StartDiscovery()
- {
- lock (_runSyncLock)
- {
- if (_currentCancellationTokenSource == null)
- {
- return;
- }
-
- var tokenSource = new CancellationTokenSource();
-
- _currentCancellationTokenSource = tokenSource;
- //Task.Factory.StartNew(() => SearchAndListen(tokenSource.Token), tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
- }
- }
-
- public static void StopDiscovery()
- {
- lock (_runSyncLock)
- {
- var tokenSource = _currentCancellationTokenSource;
-
- if (tokenSource != null)
- {
- try
- {
- tokenSource.Cancel();
- tokenSource.Dispose();
- }
- catch
- {
-
- }
-
- _currentCancellationTokenSource = null;
- }
- }
- }
-
- //checks if an IP address is a private address space as defined by RFC 1918
- public static bool IsPrivateAddressSpace(IPAddress address)
- {
- byte[] ba = address.GetAddressBytes();
-
- switch ((int)ba[0])
- {
- case 10:
- return true; //10.x.x.x
- case 172:
- return ((int)ba[1] & 16) != 0; //172.16-31.x.x
- case 192:
- return (int)ba[1] == 168; //192.168.x.x
- default:
- return false;
- }
- }
-
- public static void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint, NatProtocol protocol)
- {
- switch (protocol)
- {
- case NatProtocol.Upnp:
- //UpnpSearcher.Instance.Handle(localAddress, response, endpoint);
- break;
- case NatProtocol.Pmp:
- PmpSearcher.Instance.Handle(localAddress, response, endpoint);
- break;
- default:
- throw new ArgumentException("Unexpected protocol: " + protocol);
- }
- }
-
- public static async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol)
- {
- switch (protocol)
- {
- case NatProtocol.Upnp:
- var searcher = new UpnpSearcher(Logger, HttpClient);
- searcher.DeviceFound += Searcher_DeviceFound;
- await searcher.Handle(localAddress, deviceInfo, endpoint).ConfigureAwait(false);
- break;
- default:
- throw new ArgumentException("Unexpected protocol: " + protocol);
- }
- }
-
- private static void Searcher_DeviceFound(object sender, DeviceEventArgs e)
- {
- if (DeviceFound != null)
- {
- DeviceFound(sender, e);
- }
- }
- }
-}
diff --git a/Mono.Nat/Pmp/AsyncResults/PortMapAsyncResult.cs b/Mono.Nat/Pmp/AsyncResults/PortMapAsyncResult.cs
deleted file mode 100644
index c8ccf5435..000000000
--- a/Mono.Nat/Pmp/AsyncResults/PortMapAsyncResult.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// Authors:
-// Ben Motmans <ben.motmans@gmail.com>
-//
-// Copyright (C) 2007 Ben Motmans
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Mono.Nat.Pmp
-{
- internal class PortMapAsyncResult : AsyncResult
- {
- private Mapping mapping;
-
- internal PortMapAsyncResult (Mapping mapping, AsyncCallback callback, object asyncState)
- : base (callback, asyncState)
- {
- this.mapping = mapping;
- }
-
- internal PortMapAsyncResult (Protocol protocol, int port, int lifetime, AsyncCallback callback, object asyncState)
- : base (callback, asyncState)
- {
- this.mapping = new Mapping (protocol, port, port, lifetime);
- }
-
- internal Mapping Mapping
- {
- get { return mapping; }
- }
- }
-}
diff --git a/Mono.Nat/Pmp/PmpNatDevice.cs b/Mono.Nat/Pmp/PmpNatDevice.cs
index fb45b365b..2ef66fdbb 100644
--- a/Mono.Nat/Pmp/PmpNatDevice.cs
+++ b/Mono.Nat/Pmp/PmpNatDevice.cs
@@ -32,6 +32,7 @@ using System.Threading;
using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Logging;
namespace Mono.Nat.Pmp
{
@@ -39,11 +40,18 @@ namespace Mono.Nat.Pmp
{
private IPAddress localAddress;
private IPAddress publicAddress;
+ private ILogger _logger;
- internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress)
+ internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress, ILogger logger)
{
+ if (localAddress == null)
+ {
+ throw new ArgumentNullException("localAddress");
+ }
+
this.localAddress = localAddress;
this.publicAddress = publicAddress;
+ _logger = logger;
}
public override IPAddress LocalAddress
@@ -97,8 +105,7 @@ namespace Mono.Nat.Pmp
while (attempt < PmpConstants.RetryAttempts)
{
- await udpClient.SendAsync(buffer, buffer.Length,
- new IPEndPoint(LocalAddress, PmpConstants.ServerPort));
+ await udpClient.SendAsync(buffer, buffer.Length, new IPEndPoint(LocalAddress, PmpConstants.ServerPort));
if (attempt == 0)
{
@@ -125,9 +132,8 @@ namespace Mono.Nat.Pmp
mapping.Protocol,
mapping.PrivatePort,
e.Message);
- NatUtility.Log(message);
- var pmpException = e as MappingException;
- throw new MappingException(message, pmpException);
+ _logger.Debug(message);
+ throw e;
}
return mapping;
@@ -177,7 +183,7 @@ namespace Mono.Nat.Pmp
};
var errorMsg = errors[resultCode];
- NatUtility.Log("Error in CreatePortMapListen: " + errorMsg);
+ _logger.Debug("Error in CreatePortMapListen: " + errorMsg);
return;
}
@@ -192,7 +198,7 @@ namespace Mono.Nat.Pmp
}
catch (Exception ex)
{
- NatUtility.Logger.ErrorException("Error in CreatePortMapListen", ex);
+ _logger.ErrorException("Error in CreatePortMapListen", ex);
return;
}
}
diff --git a/Mono.Nat/Pmp/Searchers/PmpSearcher.cs b/Mono.Nat/Pmp/PmpSearcher.cs
index 2836071d0..180fb48d7 100644
--- a/Mono.Nat/Pmp/Searchers/PmpSearcher.cs
+++ b/Mono.Nat/Pmp/PmpSearcher.cs
@@ -37,33 +37,44 @@ using Mono.Nat.Pmp;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+using System.Linq;
namespace Mono.Nat
{
- internal class PmpSearcher : ISearcher
+ internal class PmpSearcher : ISearcher, IDisposable
{
- static PmpSearcher instance = new PmpSearcher();
+ private ILogger _logger;
-
- public static PmpSearcher Instance
- {
- get { return instance; }
- }
-
- private int timeout;
+ private int timeout = 250;
private DateTime nextSearch;
public event EventHandler<DeviceEventArgs> DeviceFound;
- public event EventHandler<DeviceEventArgs> DeviceLost;
- static PmpSearcher()
+ public PmpSearcher(ILogger logger)
{
+ _logger = logger;
+
CreateSocketsAndAddGateways();
}
- public static List<UdpClient> sockets;
- protected static Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
+ public void Dispose()
+ {
+ var list = sockets.ToList();
+ sockets.Clear();
+
+ foreach (var s in list)
+ {
+ using (s)
+ {
+ s.Close();
+ }
+ }
+ }
+
+ private List<UdpClient> sockets;
+ private Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
- internal static void CreateSocketsAndAddGateways()
+ private void CreateSocketsAndAddGateways()
{
sockets = new List<UdpClient>();
gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
@@ -137,11 +148,6 @@ namespace Mono.Nat
}
}
- PmpSearcher()
- {
- timeout = 250;
- }
-
public async void Search()
{
foreach (UdpClient s in sockets)
@@ -202,12 +208,13 @@ namespace Mono.Nat
return;
int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));
if (errorcode != 0)
- NatUtility.Log("Non zero error: {0}", errorcode);
+ _logger.Debug("Non zero error: {0}", errorcode);
IPAddress publicIp = new IPAddress(new byte[] { response[8], response[9], response[10], response[11] });
nextSearch = DateTime.Now.AddMinutes(5);
timeout = 250;
- OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(endpoint.Address, publicIp)));
+
+ OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(endpoint.Address, publicIp, _logger)));
}
public DateTime NextSearch
diff --git a/Mono.Nat/Upnp/Messages/DiscoverDeviceMessage.cs b/Mono.Nat/Upnp/Messages/DiscoverDeviceMessage.cs
deleted file mode 100644
index 87f5835a6..000000000
--- a/Mono.Nat/Upnp/Messages/DiscoverDeviceMessage.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Authors:
-// Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System.Net;
-using System.Text;
-
-namespace Mono.Nat.Upnp
-{
- internal static class DiscoverDeviceMessage
- {
- /// <summary>
- /// The message sent to discover all uPnP devices on the network
- /// </summary>
- /// <returns></returns>
- public static byte[] EncodeSSDP()
- {
- string s = "M-SEARCH * HTTP/1.1\r\n"
- + "HOST: 239.255.255.250:1900\r\n"
- + "MAN: \"ssdp:discover\"\r\n"
- + "MX: 3\r\n"
- + "ST: ssdp:all\r\n\r\n";
- return UTF8Encoding.ASCII.GetBytes(s);
- }
-
- public static byte[] EncodeUnicast(IPAddress gatewayAddress)
- {
- //Format obtained from http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf pg 31
- //This method only works with upnp 1.1 routers... unfortunately
- string s = "M-SEARCH * HTTP/1.1\r\n"
- + "HOST: " + gatewayAddress + ":1900\r\n"
- + "MAN: \"ssdp:discover\"\r\n"
- + "ST: ssdp:all\r\n\r\n";
- //+ "USER-AGENT: unix/5.1 UPnP/1.1 MyProduct/1.0\r\n\r\n";
- return UTF8Encoding.ASCII.GetBytes(s);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/ErrorMessage.cs b/Mono.Nat/Upnp/Messages/ErrorMessage.cs
deleted file mode 100644
index 7c0c44d8e..000000000
--- a/Mono.Nat/Upnp/Messages/ErrorMessage.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// Authors:
-// Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using MediaBrowser.Common.Net;
-
-namespace Mono.Nat.Upnp
-{
- internal class ErrorMessage : MessageBase
- {
- #region Member Variables
- public string Description
- {
- get { return this.description; }
- }
- private string description;
-
- public int ErrorCode
- {
- get { return this.errorCode; }
- }
- private int errorCode;
- #endregion
-
-
- #region Constructors
- public ErrorMessage(int errorCode, string description)
- :base(null)
- {
- this.description = description;
- this.errorCode = errorCode;
- }
- #endregion
-
- public override HttpRequestOptions Encode()
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs b/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs
deleted file mode 100644
index 48776dd6f..000000000
--- a/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Authors:
-// Alan McGovern alan.mcgovern@gmail.com
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-
-
-using System;
-using MediaBrowser.Common.Net;
-
-namespace Mono.Nat.Upnp
-{
- internal class CreatePortMappingResponseMessage : MessageBase
- {
- #region Constructors
- public CreatePortMappingResponseMessage()
- :base(null)
- {
- }
- #endregion
-
- public override HttpRequestOptions Encode()
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/UpnpMessage.cs b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
index b0264fc4a..37a46e47d 100644
--- a/Mono.Nat/Upnp/Messages/UpnpMessage.cs
+++ b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
@@ -37,7 +37,6 @@ namespace Mono.Nat.Upnp
{
internal abstract class MessageBase
{
- internal static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
protected UpnpNatDevice device;
protected MessageBase(UpnpNatDevice device)
@@ -48,7 +47,6 @@ namespace Mono.Nat.Upnp
protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
{
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
- NatUtility.Log("Initiating request to: {0}", ss);
var req = new HttpRequestOptions();
req.LogErrors = false;
@@ -84,14 +82,14 @@ namespace Mono.Nat.Upnp
get { return "POST"; }
}
- internal static void WriteFullElement(XmlWriter writer, string element, string value)
+ protected void WriteFullElement(XmlWriter writer, string element, string value)
{
writer.WriteStartElement(element);
writer.WriteString(value);
writer.WriteEndElement();
}
- internal static XmlWriter CreateWriter(StringBuilder sb)
+ protected XmlWriter CreateWriter(StringBuilder sb)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
diff --git a/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs b/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
index d9b16dd81..7c1e89d3b 100644
--- a/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
+++ b/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
@@ -46,7 +46,6 @@ namespace Mono.Nat
internal class UpnpSearcher : ISearcher
{
public event EventHandler<DeviceEventArgs> DeviceFound;
- public event EventHandler<DeviceEventArgs> DeviceLost;
private DateTime nextSearch;
private readonly ILogger _logger;
@@ -64,8 +63,11 @@ namespace Mono.Nat
public async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
{
- // No matter what, this method should never throw an exception. If something goes wrong
- // we should still be in a position to handle the next reply correctly.
+ if (localAddress == null)
+ {
+ throw new ArgumentNullException("localAddress");
+ }
+
try
{
/* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
@@ -82,7 +84,6 @@ namespace Mono.Nat
// We have an internet gateway device now
UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);
- NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
await d.GetServicesList().ConfigureAwait(false);
OnDeviceFound(new DeviceEventArgs(d));
diff --git a/Mono.Nat/Upnp/UpnpNatDevice.cs b/Mono.Nat/Upnp/UpnpNatDevice.cs
index fda990fa8..cae7f5fc8 100644
--- a/Mono.Nat/Upnp/UpnpNatDevice.cs
+++ b/Mono.Nat/Upnp/UpnpNatDevice.cs
@@ -56,6 +56,11 @@ namespace Mono.Nat.Upnp
internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger, IHttpClient httpClient)
{
+ if (localAddress == null)
+ {
+ throw new ArgumentNullException("localAddress");
+ }
+
this.LastSeen = DateTime.Now;
this.localAddress = localAddress;
@@ -72,21 +77,19 @@ namespace Mono.Nat.Upnp
// Are we going to get addresses with the "http://" attached?
if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
- NatUtility.Log("Found device at: {0}", locationDetails);
+ _logger.Debug("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string
locationDetails = locationDetails.Substring(7);
this.hostEndPoint = hostEndPoint;
- NatUtility.Log("Parsed device as: {0}", this.hostEndPoint.ToString());
-
// The service description URL is the remainder of the "locationDetails" string. The bit that was originally after the ip
// and port information
this.serviceDescriptionUrl = locationDetails.Substring(locationDetails.IndexOf('/'));
}
else
{
- NatUtility.Log("Couldn't decode address. Please send following string to the developer: ");
+ _logger.Debug("Couldn't decode address. Please send following string to the developer: ");
}
}
@@ -113,7 +116,7 @@ namespace Mono.Nat.Upnp
{
if (response.StatusCode != HttpStatusCode.OK)
{
- NatUtility.Log("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
+ _logger.Debug("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
return; // FIXME: This the best thing to do??
}
@@ -136,12 +139,11 @@ namespace Mono.Nat.Upnp
{
return;
}
- NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
+ _logger.Debug("{0}: Couldn't parse services list", HostEndPoint);
System.Threading.Thread.Sleep(10);
}
}
- NatUtility.Log("{0}: Parsed services list", HostEndPoint);
XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0");
XmlNodeList nodes = xmldoc.SelectNodes("//*/ns:serviceList", ns);
@@ -153,31 +155,35 @@ namespace Mono.Nat.Upnp
{
//If the service is a WANIPConnection, then we have what we want
string type = service["serviceType"].InnerText;
- NatUtility.Log("{0}: Found service: {1}", HostEndPoint, type);
- StringComparison c = StringComparison.OrdinalIgnoreCase;
+ _logger.Debug("{0}: Found service: {1}", HostEndPoint, type);
+
// TODO: Add support for version 2 of UPnP.
- if (type.Equals("urn:schemas-upnp-org:service:WANPPPConnection:1", c) ||
- type.Equals("urn:schemas-upnp-org:service:WANIPConnection:1", c))
+ if (string.Equals(type, "urn:schemas-upnp-org:service:WANPPPConnection:1", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(type, "urn:schemas-upnp-org:service:WANIPConnection:1", StringComparison.OrdinalIgnoreCase))
{
this.controlUrl = service["controlURL"].InnerText;
- NatUtility.Log("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
- try
+ _logger.Debug("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
+
+ Uri u;
+ if (Uri.TryCreate(controlUrl, UriKind.RelativeOrAbsolute, out u))
{
- Uri u = new Uri(controlUrl);
if (u.IsAbsoluteUri)
{
EndPoint old = hostEndPoint;
- this.hostEndPoint = new IPEndPoint(IPAddress.Parse(u.Host), u.Port);
- NatUtility.Log("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
- this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
- NatUtility.Log("{0}: New control url: {1}", HostEndPoint, controlUrl);
+ IPAddress parsedHostIpAddress;
+ if (IPAddress.TryParse(u.Host, out parsedHostIpAddress))
+ {
+ this.hostEndPoint = new IPEndPoint(parsedHostIpAddress, u.Port);
+ //_logger.Debug("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
+ this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
+ //_logger.Debug("{0}: New control url: {1}", HostEndPoint, controlUrl);
+ }
}
}
- catch
+ else
{
- NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
+ _logger.Debug("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
}
- NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
return;
}
}
diff --git a/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
index d70f72962..d6a0c2e8c 100644
--- a/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
+++ b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
@@ -1,118 +1,13 @@
-<?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>{4A4402D4-E910-443B-B8FC-2C18286A2CA0}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>OpenSubtitlesHandler</RootNamespace>
- <AssemblyName>OpenSubtitlesHandler</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- </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="Console\OSHConsole.cs" />
- <Compile Include="Interfaces\IMethodResponse.cs" />
- <Compile Include="Interfaces\MethodResponseAttr.cs" />
- <Compile Include="Languages\DetectLanguageResult.cs" />
- <Compile Include="MethodResponses\MethodResponseAddComment.cs" />
- <Compile Include="MethodResponses\MethodResponseAddRequest.cs" />
- <Compile Include="MethodResponses\MethodResponseAutoUpdate.cs" />
- <Compile Include="MethodResponses\MethodResponseCheckMovieHash.cs" />
- <Compile Include="MethodResponses\MethodResponseCheckMovieHash2.cs" />
- <Compile Include="MethodResponses\MethodResponseCheckSubHash.cs" />
- <Compile Include="MethodResponses\MethodResponseDetectLanguage.cs" />
- <Compile Include="MethodResponses\MethodResponseError.cs" />
- <Compile Include="MethodResponses\MethodResponseGetAvailableTranslations.cs" />
- <Compile Include="MethodResponses\MethodResponseGetComments.cs" />
- <Compile Include="MethodResponses\MethodResponseGetSubLanguages.cs" />
- <Compile Include="MethodResponses\MethodResponseGetTranslation.cs" />
- <Compile Include="MethodResponses\MethodResponseInsertMovie.cs" />
- <Compile Include="MethodResponses\MethodResponseInsertMovieHash.cs" />
- <Compile Include="MethodResponses\MethodResponseLogIn.cs" />
- <Compile Include="MethodResponses\MethodResponseMovieDetails.cs" />
- <Compile Include="MethodResponses\MethodResponseMovieSearch.cs" />
- <Compile Include="MethodResponses\MethodResponseNoOperation.cs" />
- <Compile Include="MethodResponses\MethodResponseReportWrongImdbMovie.cs" />
- <Compile Include="MethodResponses\MethodResponseReportWrongMovieHash.cs" />
- <Compile Include="MethodResponses\MethodResponseSearchToMail.cs" />
- <Compile Include="MethodResponses\MethodResponseServerInfo.cs" />
- <Compile Include="MethodResponses\MethodResponseSubtitleDownload.cs" />
- <Compile Include="MethodResponses\MethodResponseSubtitleSearch.cs" />
- <Compile Include="MethodResponses\MethodResponseSubtitlesVote.cs" />
- <Compile Include="MethodResponses\MethodResponseTryUploadSubtitles.cs" />
- <Compile Include="MethodResponses\MethodResponseUploadSubtitles.cs" />
- <Compile Include="MovieHasher.cs" />
- <Compile Include="Movies\CheckMovieHash2Data.cs" />
- <Compile Include="Movies\CheckMovieHash2Result.cs" />
- <Compile Include="Movies\CheckMovieHashResult.cs" />
- <Compile Include="Movies\InsertMovieHashParameters.cs" />
- <Compile Include="Movies\MovieSearchResult.cs" />
- <Compile Include="Movies\SearchToMailMovieParameter.cs" />
- <Compile Include="OpenSubtitles.cs" />
- <Compile Include="OtherTypes\GetAvailableTranslationsResult.cs" />
- <Compile Include="OtherTypes\GetCommentsResult.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="SubtitleTypes\CheckSubHashResult.cs" />
- <Compile Include="SubtitleTypes\SubtitleDownloadResult.cs" />
- <Compile Include="SubtitleTypes\SubtitleLanguage.cs" />
- <Compile Include="SubtitleTypes\SubtitleSearchParameters.cs" />
- <Compile Include="SubtitleTypes\SubtitleSearchResult.cs" />
- <Compile Include="SubtitleTypes\TryUploadSubtitlesParameters.cs" />
- <Compile Include="SubtitleTypes\UploadSubtitleInfoParameter.cs" />
- <Compile Include="SubtitleTypes\UploadSubtitleParameters.cs" />
- <Compile Include="Utilities.cs" />
- <Compile Include="XML-RPC\Enums\XmlRpcValueType.cs" />
- <Compile Include="XML-RPC\Types\XmlRpcMethodCall.cs" />
- <Compile Include="XML-RPC\Values\IXmlRpcValue.cs" />
- <Compile Include="XML-RPC\Values\XmlRpcStructMember.cs" />
- <Compile Include="XML-RPC\Values\XmlRpcValueArray.cs" />
- <Compile Include="XML-RPC\Values\XmlRpcValueBasic.cs" />
- <Compile Include="XML-RPC\Values\XmlRpcValueStruct.cs" />
- <Compile Include="XML-RPC\XmlRpcGenerator.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </ItemGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Content Include="XML-RPC\Docs\XML-RPC.txt" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets b/OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/OpenSubtitlesHandler/OpenSubtitlesHandler.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/RSSDP/CustomHttpHeaders.cs b/RSSDP/CustomHttpHeaders.cs
deleted file mode 100644
index f2412d2e7..000000000
--- a/RSSDP/CustomHttpHeaders.cs
+++ /dev/null
@@ -1,294 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Rssdp
-{
- /// <summary>
- /// Represents a custom HTTP header sent on device search response or notification messages.
- /// </summary>
- public sealed class CustomHttpHeader
- {
-
- #region Fields
-
- private string _Name;
- private string _Value;
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Full constructor.
- /// </summary>
- /// <param name="name">The field name of the header.</param>
- /// <param name="value">The value of the header</param>
- /// <remarks>
- /// <para>As per RFC 822 and 2616, the name must contain only printable ASCII characters (33-126) excluding colon (:). The value may contain any ASCII characters except carriage return or line feed.</para>
- /// </remarks>
- /// <exception cref="System.ArgumentNullException">Thrown if the name is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the name is an empty value, or contains an invalid character. Also thrown if the value contains a \r or \n character.</exception>
- public CustomHttpHeader(string name, string value)
- {
- Name = name;
- Value = value;
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Return the name of this header.
- /// </summary>
- public string Name
- {
- get { return _Name; }
- private set
- {
- EnsureValidName(value);
- _Name = value;
- }
- }
-
- /// <summary>
- /// Returns the value of this header.
- /// </summary>
- public string Value
- {
- get { return _Value; }
- private set
- {
- EnsureValidValue(value);
- _Value = value;
- }
- }
-
- #endregion
-
- #region Overrides
-
- /// <summary>
- /// Returns the header formatted for use in an HTTP message.
- /// </summary>
- /// <returns>A string representing this header in the format of 'name: value'.</returns>
- public override string ToString()
- {
- return this.Name + ": " + this.Value;
- }
-
- #endregion
-
- #region Private Methods
-
- private static void EnsureValidName(string name)
- {
- if (name == null) throw new ArgumentNullException(nameof(name), "Name cannot be null.");
- if (name.Length == 0) throw new ArgumentException("Name cannot be blank.", nameof(name));
-
- foreach (var c in name)
- {
- var b = (byte)c;
- if (c == ':' || b < 33 || b > 126) throw new ArgumentException("Name contains illegal characters.", nameof(name));
- }
- }
-
- private static void EnsureValidValue(string value)
- {
- if (String.IsNullOrEmpty(value)) return;
-
- if (value.Contains("\r") || value.Contains("\n")) throw new ArgumentException("Invalid value.", nameof(value));
- }
-
- #endregion
-
- }
-
- /// <summary>
- /// Represents a collection of custom HTTP headers, keyed by name.
- /// </summary>
- public class CustomHttpHeadersCollection : IEnumerable<CustomHttpHeader>
- {
- #region Fields
-
- private IDictionary<string, CustomHttpHeader> _Headers;
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- public CustomHttpHeadersCollection()
- {
- _Headers = new Dictionary<string, CustomHttpHeader>();
- }
-
- /// <summary>
- /// Full constructor.
- /// </summary>
- /// <param name="capacity">Specifies the initial capacity of the collection.</param>
- public CustomHttpHeadersCollection(int capacity)
- {
- _Headers = new Dictionary<string, CustomHttpHeader>(capacity);
- }
-
- #endregion
-
- #region Public Methpds
-
- /// <summary>
- /// Adds a <see cref="CustomHttpHeader"/> instance to the collection.
- /// </summary>
- /// <param name="header">The <see cref="CustomHttpHeader"/> instance to add to the collection.</param>
- /// <remarks>
- /// <para></para>
- /// </remarks>
- /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="header"/> is null.</exception>
- public void Add(CustomHttpHeader header)
- {
- if (header == null) throw new ArgumentNullException(nameof(header));
-
- lock (_Headers)
- {
- _Headers.Add(header.Name, header);
- }
- }
-
- #region Remove Overloads
-
- /// <summary>
- /// Removes the specified header instance from the collection.
- /// </summary>
- /// <param name="header">The <see cref="CustomHttpHeader"/> instance to remove from the collection.</param>
- /// <remarks>
- /// <para>Only removes the specified header if that instance was in the collection, if another header with the same name exists in the collection it is not removed.</para>
- /// </remarks>
- /// <returns>True if an item was removed from the collection, otherwise false (because it did not exist or was not the same instance).</returns>
- /// <seealso cref="Remove(string)"/>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="header"/> is null.</exception>
- public bool Remove(CustomHttpHeader header)
- {
- if (header == null) throw new ArgumentNullException(nameof(header));
-
- lock (_Headers)
- {
- if (_Headers.ContainsKey(header.Name) && _Headers[header.Name] == header)
- return _Headers.Remove(header.Name);
- }
-
- return false;
- }
-
- /// <summary>
- /// Removes the property with the specified key (<see cref="CustomHttpHeader.Name"/> from the collection.
- /// </summary>
- /// <param name="headerName">The name of the <see cref="CustomHttpHeader"/> instance to remove from the collection.</param>
- /// <returns>True if an item was removed from the collection, otherwise false (because no item exists in the collection with that key).</returns>
- /// <exception cref="System.ArgumentException">Thrown if the <paramref name="headerName"/> argument is null or empty string.</exception>
- public bool Remove(string headerName)
- {
- if (String.IsNullOrEmpty(headerName)) throw new ArgumentException("headerName cannot be null or empty.", nameof(headerName));
-
- lock (_Headers)
- {
- return _Headers.Remove(headerName);
- }
- }
-
- #endregion
-
- /// <summary>
- /// Returns a boolean indicating whether or not the specified <see cref="CustomHttpHeader"/> instance is in the collection.
- /// </summary>
- /// <param name="header">An <see cref="CustomHttpHeader"/> instance to check the collection for.</param>
- /// <returns>True if the specified instance exists in the collection, otherwise false.</returns>
- public bool Contains(CustomHttpHeader header)
- {
- if (header == null) throw new ArgumentNullException(nameof(header));
-
- lock (_Headers)
- {
- if (_Headers.ContainsKey(header.Name))
- return _Headers[header.Name] == header;
- }
-
- return false;
- }
-
- /// <summary>
- /// Returns a boolean indicating whether or not a <see cref="CustomHttpHeader"/> instance with the specified full name value exists in the collection.
- /// </summary>
- /// <param name="headerName">A string containing the full name of the <see cref="CustomHttpHeader"/> instance to check for.</param>
- /// <returns>True if an item with the specified full name exists in the collection, otherwise false.</returns>
- public bool Contains(string headerName)
- {
- if (String.IsNullOrEmpty(headerName)) throw new ArgumentException("headerName cannot be null or empty.", nameof(headerName));
-
- lock (_Headers)
- {
- return _Headers.ContainsKey(headerName);
- }
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Returns the number of items in the collection.
- /// </summary>
- public int Count
- {
- get { return _Headers.Count; }
- }
-
- /// <summary>
- /// Returns the <see cref="CustomHttpHeader"/> instance from the collection that has the specified <see cref="CustomHttpHeader.Name"/> value.
- /// </summary>
- /// <param name="name">The full name of the property to return.</param>
- /// <returns>A <see cref="CustomHttpHeader"/> instance from the collection.</returns>
- /// <exception cref="System.Collections.Generic.KeyNotFoundException">Thrown if no item exists in the collection with the specified <paramref name="name"/> value.</exception>
- public CustomHttpHeader this[string name]
- {
- get
- {
- return _Headers[name];
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- /// <summary>
- /// Returns an enumerator of <see cref="CustomHttpHeader"/> instances in this collection.
- /// </summary>
- /// <returns>An enumerator of <see cref="CustomHttpHeader"/> instances in this collection.</returns>
- public IEnumerator<CustomHttpHeader> GetEnumerator()
- {
- lock (_Headers)
- {
- return _Headers.Values.GetEnumerator();
- }
- }
-
- /// <summary>
- /// Returns an enumerator of <see cref="CustomHttpHeader"/> instances in this collection.
- /// </summary>
- /// <returns>An enumerator of <see cref="CustomHttpHeader"/> instances in this collection.</returns>
- IEnumerator IEnumerable.GetEnumerator()
- {
- lock (_Headers)
- {
- return _Headers.Values.GetEnumerator();
- }
- }
-
- #endregion
-
- }
-} \ No newline at end of file
diff --git a/RSSDP/DiscoveredSsdpDevice.cs b/RSSDP/DiscoveredSsdpDevice.cs
index 54701890c..66b8bea36 100644
--- a/RSSDP/DiscoveredSsdpDevice.cs
+++ b/RSSDP/DiscoveredSsdpDevice.cs
@@ -20,8 +20,6 @@ namespace Rssdp
private SsdpRootDevice _Device;
private DateTimeOffset _AsAt;
- private static HttpClient s_DefaultHttpClient;
-
#endregion
#region Public Properties
@@ -80,47 +78,6 @@ namespace Rssdp
return this.CacheLifetime == TimeSpan.Zero || this.AsAt.Add(this.CacheLifetime) <= DateTimeOffset.Now;
}
- /// <summary>
- /// Retrieves the device description document specified by the <see cref="DescriptionLocation"/> property.
- /// </summary>
- /// <remarks>
- /// <para>This method may choose to cache (or return cached) information if called multiple times within the <see cref="CacheLifetime"/> period.</para>
- /// </remarks>
- /// <returns>An <see cref="SsdpDevice"/> instance describing the full device details.</returns>
- public async Task<SsdpDevice> GetDeviceInfo()
- {
- var device = _Device;
- if (device == null || this.IsExpired())
- return await GetDeviceInfo(GetDefaultClient());
- else
- return device;
- }
-
- /// <summary>
- /// Retrieves the device description document specified by the <see cref="DescriptionLocation"/> property using the provided <see cref="System.Net.Http.HttpClient"/> instance.
- /// </summary>
- /// <remarks>
- /// <para>This method may choose to cache (or return cached) information if called multiple times within the <see cref="CacheLifetime"/> period.</para>
- /// <para>This method performs no error handling, if an exception occurs downloading or parsing the document it will be thrown to the calling code. Ensure you setup correct error handling for these scenarios.</para>
- /// </remarks>
- /// <param name="downloadHttpClient">A <see cref="System.Net.Http.HttpClient"/> to use when downloading the document data.</param>
- /// <returns>An <see cref="SsdpDevice"/> instance describing the full device details.</returns>
- public async Task<SsdpRootDevice> GetDeviceInfo(HttpClient downloadHttpClient)
- {
- if (_Device == null || this.IsExpired())
- {
- var rawDescriptionDocument = await downloadHttpClient.GetAsync(this.DescriptionLocation);
- rawDescriptionDocument.EnsureSuccessStatusCode();
-
- // Not using ReadAsStringAsync() here as some devices return the content type as utf-8 not UTF-8,
- // which causes an (unneccesary) exception.
- var data = await rawDescriptionDocument.Content.ReadAsByteArrayAsync();
- _Device = new SsdpRootDevice(this.DescriptionLocation, this.CacheLifetime, System.Text.UTF8Encoding.UTF8.GetString(data, 0, data.Length));
- }
-
- return _Device;
- }
-
#endregion
#region Overrides
@@ -135,36 +92,5 @@ namespace Rssdp
}
#endregion
-
- #region Private Methods
-
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Can't call dispose on the handler since we pass it to the HttpClient, which outlives the scope of this method.")]
- private static HttpClient GetDefaultClient()
- {
- if (s_DefaultHttpClient == null)
- {
- var handler = new System.Net.Http.HttpClientHandler();
- try
- {
- if (handler.SupportsAutomaticDecompression)
- handler.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip;
-
- s_DefaultHttpClient = new HttpClient(handler);
- }
- catch
- {
- if (handler != null)
- handler.Dispose();
-
- throw;
- }
- }
-
- return s_DefaultHttpClient;
- }
-
- #endregion
-
}
} \ No newline at end of file
diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs
index 1288ba721..7a0fdd45a 100644
--- a/RSSDP/DisposableManagedObjectBase.cs
+++ b/RSSDP/DisposableManagedObjectBase.cs
@@ -44,32 +44,43 @@ namespace Rssdp.Infrastructure
private set;
}
- #endregion
+ #endregion
- #region IDisposable Members
+ public string BuildMessage(string header, Dictionary<string, string> values)
+ {
+ var builder = new StringBuilder();
- /// <summary>
- /// Disposes this object instance and all internally managed resources.
- /// </summary>
- /// <remarks>
- /// <para>Sets the <see cref="IsDisposed"/> property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes.</para>
- /// </remarks>
- /// <seealso cref="IsDisposed"/>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification="We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")]
+ const string argFormat = "{0}: {1}\r\n";
+
+ builder.AppendFormat("{0}\r\n", header);
+
+ foreach (var pair in values)
+ {
+ builder.AppendFormat(argFormat, pair.Key, pair.Value);
+ }
+
+ builder.Append("\r\n");
+
+ return builder.ToString();
+ }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Disposes this object instance and all internally managed resources.
+ /// </summary>
+ /// <remarks>
+ /// <para>Sets the <see cref="IsDisposed"/> property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes.</para>
+ /// </remarks>
+ /// <seealso cref="IsDisposed"/>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification="We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")]
public void Dispose()
{
- try
- {
- IsDisposed = true;
-
- Dispose(true);
- }
- finally
- {
- GC.SuppressFinalize(this);
- }
- }
+ IsDisposed = true;
- #endregion
- }
+ Dispose(true);
+ }
+
+ #endregion
+ }
} \ No newline at end of file
diff --git a/RSSDP/GlobalSuppressions.cs b/RSSDP/GlobalSuppressions.cs
deleted file mode 100644
index b9aa0c369..000000000
--- a/RSSDP/GlobalSuppressions.cs
+++ /dev/null
Binary files differ
diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs
index 7934419b0..e841feab3 100644
--- a/RSSDP/HttpParserBase.cs
+++ b/RSSDP/HttpParserBase.cs
@@ -3,242 +3,210 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
+using System.IO;
namespace Rssdp.Infrastructure
{
- /// <summary>
- /// A base class for the <see cref="HttpResponseParser"/> and <see cref="HttpRequestParser"/> classes. Not intended for direct use.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class HttpParserBase<T> where T : new()
- {
-
- #region Fields
-
- private static readonly string[] LineTerminators = new string[] { "\r\n", "\n" };
- private static readonly char[] SeparatorCharacters = new char[] { ',', ';' };
-
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Parses the <paramref name="data"/> provided into either a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object.
- /// </summary>
- /// <param name="data">A string containing the HTTP message to parse.</param>
- /// <returns>Either a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object containing the parsed data.</returns>
- public abstract T Parse(string data);
-
- /// <summary>
- /// Parses a string containing either an HTTP request or response into a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object.
- /// </summary>
- /// <param name="message">A <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object representing the parsed message.</param>
- /// <param name="headers">A reference to the <see cref="System.Net.Http.Headers.HttpHeaders"/> collection for the <paramref name="message"/> object.</param>
- /// <param name="data">A string containing the data to be parsed.</param>
- /// <returns>An <see cref="System.Net.Http.HttpContent"/> object containing the content of the parsed message.</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification="Honestly, it's fine. MemoryStream doesn't mind.")]
- protected virtual HttpContent Parse(T message, System.Net.Http.Headers.HttpHeaders headers, string data)
- {
- if (data == null) throw new ArgumentNullException("data");
- if (data.Length == 0) throw new ArgumentException("data cannot be an empty string.", "data");
- if (!LineTerminators.Any(data.Contains)) throw new ArgumentException("data is not a valid request, it does not contain any CRLF/LF terminators.", "data");
-
- HttpContent retVal = null;
- try
- {
- var contentStream = new System.IO.MemoryStream();
- try
- {
- retVal = new StreamContent(contentStream);
-
- var lines = data.Split(LineTerminators, StringSplitOptions.None);
-
- //First line is the 'request' line containing http protocol details like method, uri, http version etc.
- ParseStatusLine(lines[0], message);
-
- int lineIndex = ParseHeaders(headers, retVal.Headers, lines);
-
- if (lineIndex < lines.Length - 1)
- {
- //Read rest of any remaining data as content.
- if (lineIndex < lines.Length - 1)
- {
- //This is inefficient in multiple ways, but not sure of a good way of correcting. Revisit.
- var body = System.Text.UTF8Encoding.UTF8.GetBytes(String.Join(null, lines, lineIndex, lines.Length - lineIndex));
- contentStream.Write(body, 0, body.Length);
- contentStream.Seek(0, System.IO.SeekOrigin.Begin);
- }
- }
- }
- catch
- {
- if (contentStream != null)
- contentStream.Dispose();
-
- throw;
- }
- }
- catch
- {
- if (retVal != null)
- retVal.Dispose();
-
- throw;
- }
-
- return retVal;
- }
-
- /// <summary>
- /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>.
- /// </summary>
- /// <param name="data">The first line of the HTTP message to be parsed.</param>
- /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param>
- protected abstract void ParseStatusLine(string data, T message);
-
- /// <summary>
- /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false).
- /// </summary>
- /// <param name="headerName">A string containing the name of the header to return the type of.</param>
- protected abstract bool IsContentHeader(string headerName);
-
- /// <summary>
- /// Parses the HTTP version text from an HTTP request or response status line and returns a <see cref="Version"/> object representing the parsed values.
- /// </summary>
- /// <param name="versionData">A string containing the HTTP version, from the message status line.</param>
- /// <returns>A <see cref="Version"/> object containing the parsed version data.</returns>
- protected static Version ParseHttpVersion(string versionData)
- {
- if (versionData == null) throw new ArgumentNullException("versionData");
-
- var versionSeparatorIndex = versionData.IndexOf('/');
- if (versionSeparatorIndex <= 0 || versionSeparatorIndex == versionData.Length) throw new ArgumentException("request header line is invalid. Http Version not supplied or incorrect format.", "versionData");
-
- return Version.Parse(versionData.Substring(versionSeparatorIndex + 1));
- }
-
- #endregion
-
- #region Private Methods
-
- /// <summary>
- /// Parses a line from an HTTP request or response message containing a header name and value pair.
- /// </summary>
- /// <param name="line">A string containing the data to be parsed.</param>
- /// <param name="headers">A reference to a <see cref="System.Net.Http.Headers.HttpHeaders"/> collection to which the parsed header will be added.</param>
- /// <param name="contentHeaders">A reference to a <see cref="System.Net.Http.Headers.HttpHeaders"/> collection for the message content, to which the parsed header will be added.</param>
- private void ParseHeader(string line, System.Net.Http.Headers.HttpHeaders headers, System.Net.Http.Headers.HttpHeaders contentHeaders)
- {
- //Header format is
- //name: value
- var headerKeySeparatorIndex = line.IndexOf(":", StringComparison.OrdinalIgnoreCase);
- var headerName = line.Substring(0, headerKeySeparatorIndex).Trim();
- var headerValue = line.Substring(headerKeySeparatorIndex + 1).Trim();
-
- //Not sure how to determine where request headers and and content headers begin,
- //at least not without a known set of headers (general headers first the content headers)
- //which seems like a bad way of doing it. So we'll assume if it's a known content header put it there
- //else use request headers.
-
- var values = ParseValues(headerValue);
- var headersToAddTo = IsContentHeader(headerName) ? contentHeaders : headers;
-
- if (values.Count > 1)
- headersToAddTo.TryAddWithoutValidation(headerName, values);
- else
- headersToAddTo.TryAddWithoutValidation(headerName, values.First());
- }
-
- private int ParseHeaders(System.Net.Http.Headers.HttpHeaders headers, System.Net.Http.Headers.HttpHeaders contentHeaders, string[] lines)
- {
- //Blank line separates headers from content, so read headers until we find blank line.
- int lineIndex = 1;
- string line = null, nextLine = null;
- while (lineIndex + 1 < lines.Length && !String.IsNullOrEmpty((line = lines[lineIndex++])))
- {
- //If the following line starts with space or tab (or any whitespace), it is really part of this header but split for human readability.
- //Combine these lines into a single comma separated style header for easier parsing.
- while (lineIndex < lines.Length && !String.IsNullOrEmpty((nextLine = lines[lineIndex])))
- {
- if (nextLine.Length > 0 && Char.IsWhiteSpace(nextLine[0]))
- {
- line += "," + nextLine.TrimStart();
- lineIndex++;
- }
- else
- break;
- }
-
- ParseHeader(line, headers, contentHeaders);
- }
- return lineIndex;
- }
-
- private static IList<string> ParseValues(string headerValue)
- {
- // This really should be better and match the HTTP 1.1 spec,
- // but this should actually be good enough for SSDP implementations
- // I think.
- var values = new List<string>();
-
- if (headerValue == "\"\"")
- {
- values.Add(String.Empty);
- return values;
- }
-
- var indexOfSeparator = headerValue.IndexOfAny(SeparatorCharacters);
- if (indexOfSeparator <= 0)
- values.Add(headerValue);
- else
- {
- var segments = headerValue.Split(SeparatorCharacters);
- if (headerValue.Contains("\""))
- {
- for (int segmentIndex = 0; segmentIndex < segments.Length; segmentIndex++)
- {
- var segment = segments[segmentIndex];
- if (segment.Trim().StartsWith("\"", StringComparison.OrdinalIgnoreCase))
- segment = CombineQuotedSegments(segments, ref segmentIndex, segment);
-
- values.Add(segment);
- }
- }
- else
- values.AddRange(segments);
- }
-
- return values;
- }
-
- private static string CombineQuotedSegments(string[] segments, ref int segmentIndex, string segment)
- {
- var trimmedSegment = segment.Trim();
- for (int index = segmentIndex; index < segments.Length; index++)
- {
- if (trimmedSegment == "\"\"" ||
- (
- trimmedSegment.EndsWith("\"", StringComparison.OrdinalIgnoreCase)
- && !trimmedSegment.EndsWith("\"\"", StringComparison.OrdinalIgnoreCase)
- && !trimmedSegment.EndsWith("\\\"", StringComparison.OrdinalIgnoreCase))
- )
- {
- segmentIndex = index;
- return trimmedSegment.Substring(1, trimmedSegment.Length - 2);
- }
-
- if (index + 1 < segments.Length)
- trimmedSegment += "," + segments[index + 1].TrimEnd();
- }
-
- segmentIndex = segments.Length;
- if (trimmedSegment.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && trimmedSegment.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
- return trimmedSegment.Substring(1, trimmedSegment.Length - 2);
- else
- return trimmedSegment;
- }
-
- #endregion
-
- }
+ /// <summary>
+ /// A base class for the <see cref="HttpResponseParser"/> and <see cref="HttpRequestParser"/> classes. Not intended for direct use.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public abstract class HttpParserBase<T> where T : new()
+ {
+
+ #region Fields
+
+ private readonly string[] LineTerminators = new string[] { "\r\n", "\n" };
+ private readonly char[] SeparatorCharacters = new char[] { ',', ';' };
+
+ #endregion
+
+ #region Public Methods
+
+ private static byte[] EmptyByteArray = new byte[]{};
+
+ /// <summary>
+ /// Parses the <paramref name="data"/> provided into either a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object.
+ /// </summary>
+ /// <param name="data">A string containing the HTTP message to parse.</param>
+ /// <returns>Either a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object containing the parsed data.</returns>
+ public abstract T Parse(string data);
+
+ /// <summary>
+ /// Parses a string containing either an HTTP request or response into a <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object.
+ /// </summary>
+ /// <param name="message">A <see cref="System.Net.Http.HttpRequestMessage"/> or <see cref="System.Net.Http.HttpResponseMessage"/> object representing the parsed message.</param>
+ /// <param name="headers">A reference to the <see cref="System.Net.Http.Headers.HttpHeaders"/> collection for the <paramref name="message"/> object.</param>
+ /// <param name="data">A string containing the data to be parsed.</param>
+ /// <returns>An <see cref="System.Net.Http.HttpContent"/> object containing the content of the parsed message.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Honestly, it's fine. MemoryStream doesn't mind.")]
+ protected virtual void Parse(T message, System.Net.Http.Headers.HttpHeaders headers, string data)
+ {
+ if (data == null) throw new ArgumentNullException("data");
+ if (data.Length == 0) throw new ArgumentException("data cannot be an empty string.", "data");
+ if (!LineTerminators.Any(data.Contains)) throw new ArgumentException("data is not a valid request, it does not contain any CRLF/LF terminators.", "data");
+
+ using (var retVal = new ByteArrayContent(EmptyByteArray))
+ {
+ var lines = data.Split(LineTerminators, StringSplitOptions.None);
+
+ //First line is the 'request' line containing http protocol details like method, uri, http version etc.
+ ParseStatusLine(lines[0], message);
+
+ ParseHeaders(headers, retVal.Headers, lines);
+ }
+ }
+
+ /// <summary>
+ /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>.
+ /// </summary>
+ /// <param name="data">The first line of the HTTP message to be parsed.</param>
+ /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param>
+ protected abstract void ParseStatusLine(string data, T message);
+
+ /// <summary>
+ /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false).
+ /// </summary>
+ /// <param name="headerName">A string containing the name of the header to return the type of.</param>
+ protected abstract bool IsContentHeader(string headerName);
+
+ /// <summary>
+ /// Parses the HTTP version text from an HTTP request or response status line and returns a <see cref="Version"/> object representing the parsed values.
+ /// </summary>
+ /// <param name="versionData">A string containing the HTTP version, from the message status line.</param>
+ /// <returns>A <see cref="Version"/> object containing the parsed version data.</returns>
+ protected Version ParseHttpVersion(string versionData)
+ {
+ if (versionData == null) throw new ArgumentNullException("versionData");
+
+ var versionSeparatorIndex = versionData.IndexOf('/');
+ if (versionSeparatorIndex <= 0 || versionSeparatorIndex == versionData.Length) throw new ArgumentException("request header line is invalid. Http Version not supplied or incorrect format.", "versionData");
+
+ return Version.Parse(versionData.Substring(versionSeparatorIndex + 1));
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Parses a line from an HTTP request or response message containing a header name and value pair.
+ /// </summary>
+ /// <param name="line">A string containing the data to be parsed.</param>
+ /// <param name="headers">A reference to a <see cref="System.Net.Http.Headers.HttpHeaders"/> collection to which the parsed header will be added.</param>
+ /// <param name="contentHeaders">A reference to a <see cref="System.Net.Http.Headers.HttpHeaders"/> collection for the message content, to which the parsed header will be added.</param>
+ private void ParseHeader(string line, System.Net.Http.Headers.HttpHeaders headers, System.Net.Http.Headers.HttpHeaders contentHeaders)
+ {
+ //Header format is
+ //name: value
+ var headerKeySeparatorIndex = line.IndexOf(":", StringComparison.OrdinalIgnoreCase);
+ var headerName = line.Substring(0, headerKeySeparatorIndex).Trim();
+ var headerValue = line.Substring(headerKeySeparatorIndex + 1).Trim();
+
+ //Not sure how to determine where request headers and and content headers begin,
+ //at least not without a known set of headers (general headers first the content headers)
+ //which seems like a bad way of doing it. So we'll assume if it's a known content header put it there
+ //else use request headers.
+
+ var values = ParseValues(headerValue);
+ var headersToAddTo = IsContentHeader(headerName) ? contentHeaders : headers;
+
+ if (values.Count > 1)
+ headersToAddTo.TryAddWithoutValidation(headerName, values);
+ else
+ headersToAddTo.TryAddWithoutValidation(headerName, values.First());
+ }
+
+ private int ParseHeaders(System.Net.Http.Headers.HttpHeaders headers, System.Net.Http.Headers.HttpHeaders contentHeaders, string[] lines)
+ {
+ //Blank line separates headers from content, so read headers until we find blank line.
+ int lineIndex = 1;
+ string line = null, nextLine = null;
+ while (lineIndex + 1 < lines.Length && !String.IsNullOrEmpty((line = lines[lineIndex++])))
+ {
+ //If the following line starts with space or tab (or any whitespace), it is really part of this header but split for human readability.
+ //Combine these lines into a single comma separated style header for easier parsing.
+ while (lineIndex < lines.Length && !String.IsNullOrEmpty((nextLine = lines[lineIndex])))
+ {
+ if (nextLine.Length > 0 && Char.IsWhiteSpace(nextLine[0]))
+ {
+ line += "," + nextLine.TrimStart();
+ lineIndex++;
+ }
+ else
+ break;
+ }
+
+ ParseHeader(line, headers, contentHeaders);
+ }
+ return lineIndex;
+ }
+
+ private IList<string> ParseValues(string headerValue)
+ {
+ // This really should be better and match the HTTP 1.1 spec,
+ // but this should actually be good enough for SSDP implementations
+ // I think.
+ var values = new List<string>();
+
+ if (headerValue == "\"\"")
+ {
+ values.Add(String.Empty);
+ return values;
+ }
+
+ var indexOfSeparator = headerValue.IndexOfAny(SeparatorCharacters);
+ if (indexOfSeparator <= 0)
+ values.Add(headerValue);
+ else
+ {
+ var segments = headerValue.Split(SeparatorCharacters);
+ if (headerValue.Contains("\""))
+ {
+ for (int segmentIndex = 0; segmentIndex < segments.Length; segmentIndex++)
+ {
+ var segment = segments[segmentIndex];
+ if (segment.Trim().StartsWith("\"", StringComparison.OrdinalIgnoreCase))
+ segment = CombineQuotedSegments(segments, ref segmentIndex, segment);
+
+ values.Add(segment);
+ }
+ }
+ else
+ values.AddRange(segments);
+ }
+
+ return values;
+ }
+
+ private string CombineQuotedSegments(string[] segments, ref int segmentIndex, string segment)
+ {
+ var trimmedSegment = segment.Trim();
+ for (int index = segmentIndex; index < segments.Length; index++)
+ {
+ if (trimmedSegment == "\"\"" ||
+ (
+ trimmedSegment.EndsWith("\"", StringComparison.OrdinalIgnoreCase)
+ && !trimmedSegment.EndsWith("\"\"", StringComparison.OrdinalIgnoreCase)
+ && !trimmedSegment.EndsWith("\\\"", StringComparison.OrdinalIgnoreCase))
+ )
+ {
+ segmentIndex = index;
+ return trimmedSegment.Substring(1, trimmedSegment.Length - 2);
+ }
+
+ if (index + 1 < segments.Length)
+ trimmedSegment += "," + segments[index + 1].TrimEnd();
+ }
+
+ segmentIndex = segments.Length;
+ if (trimmedSegment.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && trimmedSegment.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
+ return trimmedSegment.Substring(1, trimmedSegment.Length - 2);
+ else
+ return trimmedSegment;
+ }
+
+ #endregion
+
+ }
} \ No newline at end of file
diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs
index bedbbe675..8460e1ca5 100644
--- a/RSSDP/HttpRequestParser.cs
+++ b/RSSDP/HttpRequestParser.cs
@@ -38,7 +38,7 @@ namespace Rssdp.Infrastructure
{
retVal = new System.Net.Http.HttpRequestMessage();
- retVal.Content = Parse(retVal, retVal.Headers, data);
+ Parse(retVal, retVal.Headers, data);
return retVal;
}
diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs
index 5f297ca9a..8ecb944c2 100644
--- a/RSSDP/HttpResponseParser.cs
+++ b/RSSDP/HttpResponseParser.cs
@@ -16,7 +16,7 @@ namespace Rssdp.Infrastructure
#region Fields & Constants
- private static readonly string[] ContentHeaderNames = new string[]
+ private readonly string[] ContentHeaderNames = new string[]
{
"Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified"
};
@@ -37,7 +37,7 @@ namespace Rssdp.Infrastructure
{
retVal = new System.Net.Http.HttpResponseMessage();
- retVal.Content = Parse(retVal, retVal.Headers, data);
+ Parse(retVal, retVal.Headers, data);
return retVal;
}
diff --git a/RSSDP/ISsdpCommunicationsServer.cs b/RSSDP/ISsdpCommunicationsServer.cs
index 0e47974e2..b6329c1b3 100644
--- a/RSSDP/ISsdpCommunicationsServer.cs
+++ b/RSSDP/ISsdpCommunicationsServer.cs
@@ -38,11 +38,6 @@ namespace Rssdp.Infrastructure
void StopListeningForBroadcasts();
/// <summary>
- /// Stops listening for search responses on the local, unicast socket.
- /// </summary>
- void StopListeningForResponses();
-
- /// <summary>
/// Sends a message to a particular address (uni or multicast) and port.
/// </summary>
Task SendMessage(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress, CancellationToken cancellationToken);
@@ -51,6 +46,7 @@ namespace Rssdp.Infrastructure
/// Sends a message to the SSDP multicast address and port.
/// </summary>
Task SendMulticastMessage(string message, CancellationToken cancellationToken);
+ Task SendMulticastMessage(string message, int sendCount, CancellationToken cancellationToken);
#endregion
diff --git a/RSSDP/IUPnPDeviceValidator.cs b/RSSDP/IUPnPDeviceValidator.cs
deleted file mode 100644
index 8fa48df7b..000000000
--- a/RSSDP/IUPnPDeviceValidator.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-
-namespace Rssdp.Infrastructure
-{
- /// <summary>
- /// Interface for components that check an <see cref="SsdpDevice"/> object's properties meet the UPnP specification for a particular version.
- /// </summary>
- public interface IUpnpDeviceValidator
- {
- /// <summary>
- /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified root device.
- /// </summary>
- /// <param name="device">The <see cref="SsdpRootDevice"/> to validate.</param>
- System.Collections.Generic.List<string> GetValidationErrors(SsdpRootDevice device);
-
- /// <summary>
- /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified device.
- /// </summary>
- /// <param name="device">The <see cref="SsdpDevice"/> to validate.</param>
- System.Collections.Generic.List<string> GetValidationErrors(SsdpDevice device);
-
- /// <summary>
- /// Validates the specified device and throws an <see cref="System.InvalidOperationException"/> if there are any validation errors.
- /// </summary>
- void ThrowIfDeviceInvalid(SsdpDevice device);
- }
-}
diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj
index aea144a5a..994e74158 100644
--- a/RSSDP/RSSDP.csproj
+++ b/RSSDP/RSSDP.csproj
@@ -1,98 +1,13 @@
-<?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>
- <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>RSSDP</RootNamespace>
- <AssemblyName>RSSDP</AssemblyName>
- <DefaultLanguage>en-US</DefaultLanguage>
- <FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
- <TargetFrameworkVersion>v4.6</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>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Compile Include="CustomHttpHeaders.cs" />
- <Compile Include="DeviceAvailableEventArgs.cs" />
- <Compile Include="DeviceEventArgs.cs" />
- <Compile Include="DeviceUnavailableEventArgs.cs" />
- <Compile Include="DiscoveredSsdpDevice.cs" />
- <Compile Include="DisposableManagedObjectBase.cs" />
- <Compile Include="GlobalSuppressions.cs" />
- <Compile Include="HttpParserBase.cs" />
- <Compile Include="HttpRequestParser.cs" />
- <Compile Include="HttpResponseParser.cs" />
- <Compile Include="IEnumerableExtensions.cs" />
- <Compile Include="ISsdpCommunicationsServer.cs" />
- <Compile Include="ISsdpDeviceLocator.cs" />
- <Compile Include="ISsdpDevicePublisher.cs" />
- <Compile Include="IUPnPDeviceValidator.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="RequestReceivedEventArgs.cs" />
- <Compile Include="ResponseReceivedEventArgs.cs" />
- <Compile Include="SsdpCommunicationsServer.cs" />
- <Compile Include="SsdpConstants.cs" />
- <Compile Include="SsdpDevice.cs" />
- <Compile Include="SsdpDeviceExtensions.cs" />
- <Compile Include="SsdpDeviceIcon.cs" />
- <Compile Include="SsdpDeviceLocator.cs" />
- <Compile Include="SsdpDeviceLocatorBase.cs" />
- <Compile Include="SsdpDeviceProperties.cs" />
- <Compile Include="SsdpDeviceProperty.cs" />
- <Compile Include="SsdpDevicePublisher.cs" />
- <Compile Include="SsdpDevicePublisherBase.cs" />
- <Compile Include="SsdpEmbeddedDevice.cs" />
- <Compile Include="SsdpHelper.cs" />
- <Compile Include="SsdpRootDevice.cs" />
- <Compile Include="UPnP10DeviceValidator.cs" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- <Reference Include="System" />
- <Reference Include="System.Configuration" />
- <Reference Include="System.Core" />
- <Reference Include="System.Runtime.Serialization" />
- <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>
- <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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs
index 6b4f67b0b..bc8594649 100644
--- a/RSSDP/SsdpCommunicationsServer.cs
+++ b/RSSDP/SsdpCommunicationsServer.cs
@@ -124,7 +124,16 @@ namespace Rssdp.Infrastructure
lock (_BroadcastListenSocketSynchroniser)
{
if (_BroadcastListenSocket == null)
- _BroadcastListenSocket = ListenForBroadcastsAsync();
+ {
+ try
+ {
+ _BroadcastListenSocket = ListenForBroadcastsAsync();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in BeginListeningForBroadcasts", ex);
+ }
+ }
}
}
}
@@ -135,12 +144,11 @@ namespace Rssdp.Infrastructure
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
public void StopListeningForBroadcasts()
{
- ThrowIfDisposed();
-
lock (_BroadcastListenSocketSynchroniser)
{
if (_BroadcastListenSocket != null)
{
+ _logger.Info("{0} disposing _BroadcastListenSocket.", GetType().Name);
_BroadcastListenSocket.Dispose();
_BroadcastListenSocket = null;
}
@@ -227,10 +235,15 @@ namespace Rssdp.Infrastructure
}
}
+ public Task SendMulticastMessage(string message, CancellationToken cancellationToken)
+ {
+ return SendMulticastMessage(message, SsdpConstants.UdpResendCount, cancellationToken);
+ }
+
/// <summary>
/// Sends a message to the SSDP multicast address and port.
/// </summary>
- public async Task SendMulticastMessage(string message, CancellationToken cancellationToken)
+ public async Task SendMulticastMessage(string message, int sendCount, CancellationToken cancellationToken)
{
if (message == null) throw new ArgumentNullException("messageData");
@@ -243,7 +256,7 @@ namespace Rssdp.Infrastructure
EnsureSendSocketCreated();
// SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP.
- for (var i = 0; i < SsdpConstants.UdpResendCount; i++)
+ for (var i = 0; i < sendCount; i++)
{
await SendMessageIfSocketNotDisposed(messageData, new IpEndPointInfo
{
@@ -262,8 +275,6 @@ namespace Rssdp.Infrastructure
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
public void StopListeningForResponses()
{
- ThrowIfDisposed();
-
lock (_SendSocketSynchroniser)
{
if (_sendSockets != null)
@@ -271,8 +282,11 @@ namespace Rssdp.Infrastructure
var sockets = _sendSockets.ToList();
_sendSockets = null;
+ _logger.Info("{0} Disposing {1} sendSockets", GetType().Name, sockets.Count);
+
foreach (var socket in sockets)
{
+ _logger.Info("{0} disposing sendSocket from {1}", GetType().Name, socket.LocalIPAddress);
socket.Dispose();
}
}
@@ -307,25 +321,9 @@ namespace Rssdp.Infrastructure
{
if (disposing)
{
- lock (_BroadcastListenSocketSynchroniser)
- {
- if (_BroadcastListenSocket != null)
- _BroadcastListenSocket.Dispose();
- }
+ StopListeningForBroadcasts();
- lock (_SendSocketSynchroniser)
- {
- if (_sendSockets != null)
- {
- var sockets = _sendSockets.ToList();
- _sendSockets = null;
-
- foreach (var socket in sockets)
- {
- socket.Dispose();
- }
- }
- }
+ StopListeningForResponses();
}
}
@@ -333,18 +331,18 @@ namespace Rssdp.Infrastructure
#region Private Methods
- private async Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination, CancellationToken cancellationToken)
+ private Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination, CancellationToken cancellationToken)
{
var sockets = _sendSockets;
if (sockets != null)
{
sockets = sockets.ToList();
- foreach (var socket in sockets)
- {
- await SendFromSocket(socket, messageData, destination, cancellationToken).ConfigureAwait(false);
- }
+ var tasks = sockets.Select(s => SendFromSocket(s, messageData, destination, cancellationToken));
+ return Task.WhenAll(tasks);
}
+
+ return Task.CompletedTask;
}
private ISocket ListenForBroadcastsAsync()
@@ -395,35 +393,37 @@ namespace Rssdp.Infrastructure
private void ListenToSocket(ISocket socket)
{
// Tasks are captured to local variables even if we don't use them just to avoid compiler warnings.
- var t = Task.Run(async () =>
- {
- var cancelled = false;
- var receiveBuffer = new byte[8192];
+ var t = Task.Run(() => ListenToSocketInternal(socket));
+ }
+
+ private async Task ListenToSocketInternal(ISocket socket)
+ {
+ var cancelled = false;
+ var receiveBuffer = new byte[8192];
- while (!cancelled)
+ while (!cancelled && !IsDisposed)
+ {
+ try
{
- try
- {
- var result = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
+ var result = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
- if (result.ReceivedBytes > 0)
- {
- // Strange cannot convert compiler error here if I don't explicitly
- // assign or cast to Action first. Assignment is easier to read,
- // so went with that.
- ProcessMessage(System.Text.UTF8Encoding.UTF8.GetString(result.Buffer, 0, result.ReceivedBytes), result.RemoteEndPoint, result.LocalIPAddress);
- }
- }
- catch (ObjectDisposedException)
- {
- cancelled = true;
- }
- catch (TaskCanceledException)
+ if (result.ReceivedBytes > 0)
{
- cancelled = true;
+ // Strange cannot convert compiler error here if I don't explicitly
+ // assign or cast to Action first. Assignment is easier to read,
+ // so went with that.
+ ProcessMessage(System.Text.UTF8Encoding.UTF8.GetString(result.Buffer, 0, result.ReceivedBytes), result.RemoteEndPoint, result.LocalIPAddress);
}
}
- });
+ catch (ObjectDisposedException)
+ {
+ cancelled = true;
+ }
+ catch (TaskCanceledException)
+ {
+ cancelled = true;
+ }
+ }
}
private void EnsureSendSocketCreated()
@@ -445,7 +445,7 @@ namespace Rssdp.Infrastructure
//Responses start with the HTTP version, prefixed with HTTP/ while
//requests start with a method which can vary and might be one we haven't
//seen/don't know. We'll check if this message is a request or a response
- //by checking for the static HTTP/ prefix on the start of the message.
+ //by checking for the HTTP/ prefix on the start of the message.
if (data.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase))
{
HttpResponseMessage responseMessage = null;
@@ -453,7 +453,7 @@ namespace Rssdp.Infrastructure
{
responseMessage = _ResponseParser.Parse(data);
}
- catch (ArgumentException ex)
+ catch (ArgumentException)
{
// Ignore invalid packets.
}
@@ -468,7 +468,7 @@ namespace Rssdp.Infrastructure
{
requestMessage = _RequestParser.Parse(data);
}
- catch (ArgumentException ex)
+ catch (ArgumentException)
{
// Ignore invalid packets.
}
diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs
index 65d9be139..ef6869c8b 100644
--- a/RSSDP/SsdpDevice.cs
+++ b/RSSDP/SsdpDevice.cs
@@ -25,8 +25,6 @@ namespace Rssdp
private string _DeviceType;
private string _DeviceTypeNamespace;
private int _DeviceVersion;
- private SsdpDevicePropertiesCollection _CustomProperties;
- private CustomHttpHeadersCollection _CustomResponseHeaders;
private IList<SsdpDevice> _Devices;
@@ -61,36 +59,23 @@ namespace Rssdp
_DeviceType = SsdpConstants.UpnpDeviceTypeBasicDevice;
_DeviceVersion = 1;
- this.Icons = new List<SsdpDeviceIcon>();
_Devices = new List<SsdpDevice>();
this.Devices = new ReadOnlyCollection<SsdpDevice>(_Devices);
- _CustomResponseHeaders = new CustomHttpHeadersCollection();
- _CustomProperties = new SsdpDevicePropertiesCollection();
}
- /// <summary>
- /// Deserialisation constructor.
- /// </summary>
- /// <remarks><para>Uses the provided XML string and parent device properties to set the properties of the object. The XML provided must be a valid UPnP device description document.</para></remarks>
- /// <param name="deviceDescriptionXml">A UPnP device description XML document.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="deviceDescriptionXml"/> argument is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the <paramref name="deviceDescriptionXml"/> argument is empty.</exception>
- protected SsdpDevice(string deviceDescriptionXml)
- : this()
+ #endregion
+
+ public SsdpRootDevice ToRootDevice()
{
- if (deviceDescriptionXml == null) throw new ArgumentNullException("deviceDescriptionXml");
- if (deviceDescriptionXml.Length == 0) throw new ArgumentException("deviceDescriptionXml cannot be an empty string.", "deviceDescriptionXml");
+ var device = this;
- using (var ms = new System.IO.MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(deviceDescriptionXml)))
- {
- var reader = XmlReader.Create(ms);
+ var rootDevice = device as SsdpRootDevice;
+ if (rootDevice == null)
+ rootDevice = ((SsdpEmbeddedDevice)device).RootDevice;
- LoadDeviceProperties(reader, this);
- }
+ return rootDevice;
}
-
- #endregion
-
+
#region Public Properties
#region UPnP Device Description Properties
@@ -269,15 +254,6 @@ namespace Rssdp
#endregion
/// <summary>
- /// Returns a list of icons (images) that can be used to display this device. Optional, but recommended you provide at least one at 48x48 pixels.
- /// </summary>
- public IList<SsdpDeviceIcon> Icons
- {
- get;
- private set;
- }
-
- /// <summary>
/// Returns a read-only enumerable set of <see cref="SsdpDevice"/> objects representing children of this device. Child devices are optional.
/// </summary>
/// <seealso cref="AddDevice"/>
@@ -288,32 +264,6 @@ namespace Rssdp
private set;
}
- /// <summary>
- /// Returns a dictionary of <see cref="SsdpDeviceProperty"/> objects keyed by <see cref="SsdpDeviceProperty.FullName"/>. Each value represents a custom property in the device description document.
- /// </summary>
- public SsdpDevicePropertiesCollection CustomProperties
- {
- get
- {
- return _CustomProperties;
- }
- }
-
- /// <summary>
- /// Provides a list of additional information to provide about this device in search response and notification messages.
- /// </summary>
- /// <remarks>
- /// <para>The headers included here are included in the (HTTP headers) for search response and alive notifications sent in relation to this device.</para>
- /// <para>Only values specified directly on this <see cref="SsdpDevice"/> instance will be included, headers from ancestors are not automatically included.</para>
- /// </remarks>
- public CustomHttpHeadersCollection CustomResponseHeaders
- {
- get
- {
- return _CustomResponseHeaders;
- }
- }
-
#endregion
#region Public Methods
@@ -401,389 +351,6 @@ namespace Rssdp
handlers(this, new DeviceEventArgs(device));
}
- /// <summary>
- /// Writes this device to the specified <see cref="System.Xml.XmlWriter"/> as a device node and it's content.
- /// </summary>
- /// <param name="writer">The <see cref="System.Xml.XmlWriter"/> to output to.</param>
- /// <param name="device">The <see cref="SsdpDevice"/> to write out.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="writer"/> or <paramref name="device"/> argument is null.</exception>
- protected virtual void WriteDeviceDescriptionXml(XmlWriter writer, SsdpDevice device)
- {
- if (writer == null) throw new ArgumentNullException("writer");
- if (device == null) throw new ArgumentNullException("device");
-
- writer.WriteStartElement("device");
-
- if (!String.IsNullOrEmpty(device.FullDeviceType))
- WriteNodeIfNotEmpty(writer, "deviceType", device.FullDeviceType);
-
- WriteNodeIfNotEmpty(writer, "friendlyName", device.FriendlyName);
- WriteNodeIfNotEmpty(writer, "manufacturer", device.Manufacturer);
- WriteNodeIfNotEmpty(writer, "manufacturerURL", device.ManufacturerUrl);
- WriteNodeIfNotEmpty(writer, "modelDescription", device.ModelDescription);
- WriteNodeIfNotEmpty(writer, "modelName", device.ModelName);
- WriteNodeIfNotEmpty(writer, "modelNumber", device.ModelNumber);
- WriteNodeIfNotEmpty(writer, "modelURL", device.ModelUrl);
- WriteNodeIfNotEmpty(writer, "presentationURL", device.PresentationUrl);
- WriteNodeIfNotEmpty(writer, "serialNumber", device.SerialNumber);
- WriteNodeIfNotEmpty(writer, "UDN", device.Udn);
- WriteNodeIfNotEmpty(writer, "UPC", device.Upc);
-
- WriteCustomProperties(writer, device);
- WriteIcons(writer, device);
- WriteChildDevices(writer, device);
-
- writer.WriteEndElement();
- }
-
- /// <summary>
- /// Converts a string to a <see cref="Uri"/>, or returns null if the string provided is null.
- /// </summary>
- /// <param name="value">The string value to convert.</param>
- /// <returns>A <see cref="Uri"/>.</returns>
- protected static Uri StringToUri(string value)
- {
- if (!String.IsNullOrEmpty(value))
- return new Uri(value, UriKind.RelativeOrAbsolute);
-
- return null;
- }
-
- #endregion
-
- #region Private Methods
-
- #region Serialisation Methods
-
- private static void WriteCustomProperties(XmlWriter writer, SsdpDevice device)
- {
- foreach (var prop in device.CustomProperties)
- {
- writer.WriteElementString(prop.Namespace, prop.Name, SsdpConstants.SsdpDeviceDescriptionXmlNamespace, prop.Value);
- }
- }
-
- private static void WriteIcons(XmlWriter writer, SsdpDevice device)
- {
- if (device.Icons.Count > 0)
- {
- writer.WriteStartElement("iconList");
-
- foreach (var icon in device.Icons)
- {
- writer.WriteStartElement("icon");
-
- writer.WriteElementString("mimetype", icon.MimeType);
- writer.WriteElementString("width", icon.Width.ToString());
- writer.WriteElementString("height", icon.Height.ToString());
- writer.WriteElementString("depth", icon.ColorDepth.ToString());
- writer.WriteElementString("url", icon.Url.ToString());
-
- writer.WriteEndElement();
- }
-
- writer.WriteEndElement();
- }
- }
-
- private void WriteChildDevices(XmlWriter writer, SsdpDevice parentDevice)
- {
- if (parentDevice.Devices.Count > 0)
- {
- writer.WriteStartElement("deviceList");
-
- foreach (var device in parentDevice.Devices)
- {
- WriteDeviceDescriptionXml(writer, device);
- }
-
- writer.WriteEndElement();
- }
- }
-
- private static void WriteNodeIfNotEmpty(XmlWriter writer, string nodeName, string value)
- {
- if (!String.IsNullOrEmpty(value))
- writer.WriteElementString(nodeName, value);
- }
-
- private static void WriteNodeIfNotEmpty(XmlWriter writer, string nodeName, Uri value)
- {
- if (value != null)
- writer.WriteElementString(nodeName, value.ToString());
- }
-
- #endregion
-
- #region Deserialisation Methods
-
- private void LoadDeviceProperties(XmlReader reader, SsdpDevice device)
- {
- ReadUntilDeviceNode(reader);
-
- while (!reader.EOF)
- {
- if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "device")
- {
- reader.Read();
- break;
- }
-
- if (!SetPropertyFromReader(reader, device))
- reader.Read();
- }
- }
-
- private static void ReadUntilDeviceNode(XmlReader reader)
- {
- while (!reader.EOF && (reader.LocalName != "device" || reader.NodeType != XmlNodeType.Element))
- {
- reader.Read();
- }
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Yes, there is a large switch statement, not it's not really complex and doesn't really need to be rewritten at this point.")]
- private bool SetPropertyFromReader(XmlReader reader, SsdpDevice device)
- {
- switch (reader.LocalName)
- {
- case "friendlyName":
- device.FriendlyName = reader.ReadElementContentAsString();
- break;
-
- case "manufacturer":
- device.Manufacturer = reader.ReadElementContentAsString();
- break;
-
- case "manufacturerURL":
- device.ManufacturerUrl = StringToUri(reader.ReadElementContentAsString());
- break;
-
- case "modelDescription":
- device.ModelDescription = reader.ReadElementContentAsString();
- break;
-
- case "modelName":
- device.ModelName = reader.ReadElementContentAsString();
- break;
-
- case "modelNumber":
- device.ModelNumber = reader.ReadElementContentAsString();
- break;
-
- case "modelURL":
- device.ModelUrl = StringToUri(reader.ReadElementContentAsString());
- break;
-
- case "presentationURL":
- device.PresentationUrl = StringToUri(reader.ReadElementContentAsString());
- break;
-
- case "serialNumber":
- device.SerialNumber = reader.ReadElementContentAsString();
- break;
-
- case "UDN":
- device.Udn = reader.ReadElementContentAsString();
- SetUuidFromUdn(device);
- break;
-
- case "UPC":
- device.Upc = reader.ReadElementContentAsString();
- break;
-
- case "deviceType":
- SetDeviceTypePropertiesFromFullDeviceType(device, reader.ReadElementContentAsString());
- break;
-
- case "iconList":
- reader.Read();
- LoadIcons(reader, device);
- break;
-
- case "deviceList":
- reader.Read();
- LoadChildDevices(reader, device);
- break;
-
- case "serviceList":
- reader.Skip();
- break;
-
- default:
- if (reader.NodeType == XmlNodeType.Element && reader.Name != "device" && reader.Name != "icon")
- {
- AddCustomProperty(reader, device);
- break;
- }
- else
- return false;
- }
- return true;
- }
-
- private static void SetDeviceTypePropertiesFromFullDeviceType(SsdpDevice device, string value)
- {
- if (String.IsNullOrEmpty(value) || !value.Contains(":"))
- device.DeviceType = value;
- else
- {
- var parts = value.Split(':');
- if (parts.Length == 5)
- {
- int deviceVersion = 1;
- if (Int32.TryParse(parts[4], out deviceVersion))
- {
- device.DeviceTypeNamespace = parts[1];
- device.DeviceType = parts[3];
- device.DeviceVersion = deviceVersion;
- }
- else
- device.DeviceType = value;
- }
- else
- device.DeviceType = value;
- }
- }
-
- private static void SetUuidFromUdn(SsdpDevice device)
- {
- if (device.Udn != null && device.Udn.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
- device.Uuid = device.Udn.Substring(5).Trim();
- else
- device.Uuid = device.Udn;
- }
-
- private static void LoadIcons(XmlReader reader, SsdpDevice device)
- {
- while (!reader.EOF)
- {
- while (!reader.EOF && reader.NodeType != XmlNodeType.Element)
- {
- reader.Read();
- }
-
- if (reader.LocalName != "icon") break;
-
- while (reader.Name == "icon")
- {
- var icon = new SsdpDeviceIcon();
- LoadIconProperties(reader, icon);
- device.Icons.Add(icon);
-
- reader.Read();
- }
- }
- }
-
- private static void LoadIconProperties(XmlReader reader, SsdpDeviceIcon icon)
- {
- while (!reader.EOF)
- {
- if (reader.NodeType != XmlNodeType.Element)
- {
- if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "icon") break;
-
- reader.Read();
- continue;
- }
-
- switch (reader.LocalName)
- {
- case "depth":
- icon.ColorDepth = reader.ReadElementContentAsInt();
- break;
-
- case "height":
- icon.Height = reader.ReadElementContentAsInt();
- break;
-
- case "width":
- icon.Width = reader.ReadElementContentAsInt();
- break;
-
- case "mimetype":
- icon.MimeType = reader.ReadElementContentAsString();
- break;
-
- case "url":
- icon.Url = StringToUri(reader.ReadElementContentAsString());
- break;
-
- }
-
- reader.Read();
- }
- }
-
- private void LoadChildDevices(XmlReader reader, SsdpDevice device)
- {
- while (!reader.EOF && reader.NodeType != XmlNodeType.Element)
- {
- reader.Read();
- }
-
- while (!reader.EOF)
- {
- while (!reader.EOF && reader.NodeType != XmlNodeType.Element)
- {
- reader.Read();
- }
-
- if (reader.LocalName == "device")
- {
- var childDevice = new SsdpEmbeddedDevice();
- LoadDeviceProperties(reader, childDevice);
- device.AddDevice(childDevice);
- }
- else
- break;
- }
- }
-
- private static void AddCustomProperty(XmlReader reader, SsdpDevice device)
- {
- // If the property is an empty element, there is no value to read
- // Advance the reader and return
- if (reader.IsEmptyElement)
- {
- reader.Read();
- return;
- }
-
- var newProp = new SsdpDeviceProperty() { Namespace = reader.Prefix, Name = reader.LocalName };
- int depth = reader.Depth;
- reader.Read();
- while (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.Comment)
- {
- reader.Read();
- }
-
- if (reader.NodeType != XmlNodeType.CDATA && reader.NodeType != XmlNodeType.Text)
- {
- while (!reader.EOF && (reader.NodeType != XmlNodeType.EndElement || reader.Name != newProp.Name || reader.Prefix != newProp.Namespace || reader.Depth != depth))
- {
- reader.Read();
- }
- if (!reader.EOF)
- reader.Read();
- return;
- }
-
- newProp.Value = reader.Value;
-
- // We don't support complex nested types or repeat/multi-value properties
- if (!device.CustomProperties.Contains(newProp.FullName))
- device.CustomProperties.Add(newProp);
- }
-
- #endregion
-
- //private bool ChildDeviceExists(SsdpDevice device)
- //{
- // return (from d in _Devices where device.Uuid == d.Uuid select d).Any();
- //}
-
#endregion
}
diff --git a/RSSDP/SsdpDeviceExtensions.cs b/RSSDP/SsdpDeviceExtensions.cs
deleted file mode 100644
index 6e1b45646..000000000
--- a/RSSDP/SsdpDeviceExtensions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Rssdp
-{
- /// <summary>
- /// Extensions for <see cref="SsdpDevice"/> and derived types.
- /// </summary>
- public static class SsdpDeviceExtensions
- {
-
- /// <summary>
- /// Returns the root device associated with a device instance derived from <see cref="SsdpDevice"/>.
- /// </summary>
- /// <param name="device">The device instance to find the <see cref="SsdpRootDevice"/> for.</param>
- /// <remarks>
- /// <para>The <paramref name="device"/> must be or inherit from <see cref="SsdpRootDevice"/> or <see cref="SsdpEmbeddedDevice"/>, otherwise an <see cref="System.InvalidCastException"/> will occur.</para>
- /// <para>May return null if the <paramref name="device"/> instance is an embedded device not yet associated with a <see cref="SsdpRootDevice"/> instance yet.</para>
- /// <para>If <paramref name="device"/> is an instance of <see cref="SsdpRootDevice"/> (or derives from it), returns the same instance cast to <see cref="SsdpRootDevice"/>.</para>
- /// </remarks>
- /// <returns>The <see cref="SsdpRootDevice"/> instance associated with the device instance specified, or null otherwise.</returns>
- /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="device"/> is null.</exception>
- /// <exception cref="System.InvalidCastException">Thrown if <paramref name="device"/> is not an instance of or dervied from either <see cref="SsdpRootDevice"/> or <see cref="SsdpEmbeddedDevice"/>.</exception>
- public static SsdpRootDevice ToRootDevice(this SsdpDevice device)
- {
- if (device == null) throw new System.ArgumentNullException("device");
-
- var rootDevice = device as SsdpRootDevice;
- if (rootDevice == null)
- rootDevice = ((SsdpEmbeddedDevice)device).RootDevice;
-
- return rootDevice;
- }
- }
-}
diff --git a/RSSDP/SsdpDeviceIcon.cs b/RSSDP/SsdpDeviceIcon.cs
deleted file mode 100644
index 3ed707c80..000000000
--- a/RSSDP/SsdpDeviceIcon.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Rssdp
-{
- /// <summary>
- /// Represents an icon published by an <see cref="SsdpDevice"/>.
- /// </summary>
- public sealed class SsdpDeviceIcon
- {
- /// <summary>
- /// The mime type for the image data returned by the <see cref="Url"/> property.
- /// </summary>
- /// <remarks>
- /// <para>Required. Icon's MIME type (cf. RFC 2045, 2046, and 2387). Single MIME image type. At least one icon should be of type “image/png” (Portable Network Graphics, see IETF RFC 2083).</para>
- /// </remarks>
- /// <seealso cref="Url"/>
- public string MimeType { get; set; }
-
- /// <summary>
- /// The URL that can be called with an HTTP GET command to retrieve the image data.
- /// </summary>
- /// <remarks>
- /// <para>Required. May be relative to base URL. Specified by UPnP vendor. Single URL.</para>
- /// </remarks>
- /// <seealso cref="MimeType"/>
- public Uri Url { get; set; }
-
- /// <summary>
- /// The width of the image in pixels.
- /// </summary>
- /// <remarks><para>Required, must be greater than zero.</para></remarks>
- public int Width { get; set; }
-
- /// <summary>
- /// The height of the image in pixels.
- /// </summary>
- /// <remarks><para>Required, must be greater than zero.</para></remarks>
- public int Height { get; set; }
-
- /// <summary>
- /// The colour depth of the image.
- /// </summary>
- /// <remarks><para>Required, must be greater than zero.</para></remarks>
- public int ColorDepth { get; set; }
-
- }
-}
diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs
index 8fe4d4a14..6a61a52f3 100644
--- a/RSSDP/SsdpDeviceLocator.cs
+++ b/RSSDP/SsdpDeviceLocator.cs
@@ -1,35 +1,624 @@
-using System;
+using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
-using Rssdp.Infrastructure;
-namespace Rssdp
+namespace Rssdp.Infrastructure
{
- // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
- // Be careful to check any changes compile and work for all platform projects it is shared in.
-
- /// <summary>
- /// Allows you to search the network for a particular device, device types, or UPnP service types. Also listenings for broadcast notifications of device availability and raises events to indicate changes in status.
- /// </summary>
- public sealed class SsdpDeviceLocator : SsdpDeviceLocatorBase
- {
-
- /// <summary>
- /// Default constructor. Constructs a new instance using the default <see cref="ISsdpCommunicationsServer"/> and <see cref="ISocketFactory"/> implementations for this platform.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="Can't expose along exception paths here (exceptions should be very rare anyway, and probably fatal too) and we shouldn't dipose the items we pass to base in any other case.")]
- public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFacatory) : base(communicationsServer, timerFacatory)
- {
- // This is not the problem you are looking for;
- // Yes, this is poor man's dependency injection which some call an anti-pattern.
- // However, it makes the library really simple to get started with or to use if the calling code isn't using IoC/DI.
- // The fact we have injected dependencies is really an internal architectural implementation detail to allow for the
- // cross platform and testing concerns of this library. It shouldn't be something calling code worries about and is
- // not a deliberate extension point, except where adding new platform support in which case...
- // There is a constructor that takes a manually injected dependency anyway, so proper DI using
- // a container or whatever can be done anyway.
- }
- }
+ /// <summary>
+ /// Allows you to search the network for a particular device, device types, or UPnP service types. Also listenings for broadcast notifications of device availability and raises events to indicate changes in status.
+ /// </summary>
+ public class SsdpDeviceLocator : DisposableManagedObjectBase
+ {
+
+ #region Fields & Constants
+
+ private List<DiscoveredSsdpDevice> _Devices;
+ private ISsdpCommunicationsServer _CommunicationsServer;
+
+ private ITimer _BroadcastTimer;
+ private ITimerFactory _timerFactory;
+ private object _timerLock = new object();
+
+ private readonly TimeSpan DefaultSearchWaitTime = TimeSpan.FromSeconds(4);
+ private readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory)
+ {
+ if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
+
+ _CommunicationsServer = communicationsServer;
+ _timerFactory = timerFactory;
+ _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived;
+
+ _Devices = new List<DiscoveredSsdpDevice>();
+ }
+
+ #endregion
+
+ #region Events
+
+ /// <summary>
+ /// Raised for when
+ /// <list type="bullet">
+ /// <item>An 'alive' notification is received that a device, regardless of whether or not that device is not already in the cache or has previously raised this event.</item>
+ /// <item>For each item found during a device <see cref="SearchAsync()"/> (cached or not), allowing clients to respond to found devices before the entire search is complete.</item>
+ /// <item>Only if the notification type matches the <see cref="NotificationFilter"/> property. By default the filter is null, meaning all notifications raise events (regardless of ant </item>
+ /// </list>
+ /// <para>This event may be raised from a background thread, if interacting with UI or other objects with specific thread affinity invoking to the relevant thread is required.</para>
+ /// </summary>
+ /// <seealso cref="NotificationFilter"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="StopListeningForNotifications"/>
+ public event EventHandler<DeviceAvailableEventArgs> DeviceAvailable;
+
+ /// <summary>
+ /// Raised when a notification is received that indicates a device has shutdown or otherwise become unavailable.
+ /// </summary>
+ /// <remarks>
+ /// <para>Devices *should* broadcast these types of notifications, but not all devices do and sometimes (in the event of power loss for example) it might not be possible for a device to do so. You should also implement error handling when trying to contact a device, even if RSSDP is reporting that device as available.</para>
+ /// <para>This event is only raised if the notification type matches the <see cref="NotificationFilter"/> property. A null or empty string for the <see cref="NotificationFilter"/> will be treated as no filter and raise the event for all notifications.</para>
+ /// <para>The <see cref="DeviceUnavailableEventArgs.DiscoveredDevice"/> property may contain either a fully complete <see cref="DiscoveredSsdpDevice"/> instance, or one containing just a USN and NotificationType property. Full information is available if the device was previously discovered and cached, but only partial information if a byebye notification was received for a previously unseen or expired device.</para>
+ /// <para>This event may be raised from a background thread, if interacting with UI or other objects with specific thread affinity invoking to the relevant thread is required.</para>
+ /// </remarks>
+ /// <seealso cref="NotificationFilter"/>
+ /// <seealso cref="DeviceAvailable"/>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="StopListeningForNotifications"/>
+ public event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable;
+
+ #endregion
+
+ #region Public Methods
+
+ #region Search Overloads
+
+ public void RestartBroadcastTimer(TimeSpan dueTime, TimeSpan period)
+ {
+ lock (_timerLock)
+ {
+ if (_BroadcastTimer == null)
+ {
+ _BroadcastTimer = _timerFactory.Create(OnBroadcastTimerCallback, null, dueTime, period);
+ }
+ else
+ {
+ _BroadcastTimer.Change(dueTime, period);
+ }
+ }
+ }
+
+ public void DisposeBroadcastTimer()
+ {
+ lock (_timerLock)
+ {
+ if (_BroadcastTimer != null)
+ {
+ _BroadcastTimer.Dispose();
+ _BroadcastTimer = null;
+ }
+ }
+ }
+
+ private async void OnBroadcastTimerCallback(object state)
+ {
+ StartListeningForNotifications();
+ RemoveExpiredDevicesFromCache();
+
+ try
+ {
+ await SearchAsync(CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+
+ }
+ }
+
+ /// <summary>
+ /// Performs a search for all devices using the default search timeout.
+ /// </summary>
+ private Task SearchAsync(CancellationToken cancellationToken)
+ {
+ return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, DefaultSearchWaitTime, cancellationToken);
+ }
+
+ /// <summary>
+ /// Performs a search for the specified search target (criteria) and default search timeout.
+ /// </summary>
+ /// <param name="searchTarget">The criteria for the search. Value can be;
+ /// <list type="table">
+ /// <item><term>Root devices</term><description>upnp:rootdevice</description></item>
+ /// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item>
+ /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item>
+ /// </list>
+ /// </param>
+ private Task SearchAsync(string searchTarget)
+ {
+ return SearchAsync(searchTarget, DefaultSearchWaitTime, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Performs a search for all devices using the specified search timeout.
+ /// </summary>
+ /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 seconds is recommended by the UPnP 1.1 specification, this method requires the value be greater 1 second if it is not zero. Specify TimeSpan.Zero to return only devices already in the cache.</param>
+ private Task SearchAsync(TimeSpan searchWaitTime)
+ {
+ return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, searchWaitTime, CancellationToken.None);
+ }
+
+ private Task SearchAsync(string searchTarget, TimeSpan searchWaitTime, CancellationToken cancellationToken)
+ {
+ if (searchTarget == null) throw new ArgumentNullException("searchTarget");
+ if (searchTarget.Length == 0) throw new ArgumentException("searchTarget cannot be an empty string.", "searchTarget");
+ if (searchWaitTime.TotalSeconds < 0) throw new ArgumentException("searchWaitTime must be a positive time.");
+ if (searchWaitTime.TotalSeconds > 0 && searchWaitTime.TotalSeconds <= 1) throw new ArgumentException("searchWaitTime must be zero (if you are not using the result and relying entirely in the events), or greater than one second.");
+
+ ThrowIfDisposed();
+
+ return BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime), cancellationToken);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Starts listening for broadcast notifications of service availability.
+ /// </summary>
+ /// <remarks>
+ /// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para>
+ /// </remarks>
+ /// <seealso cref="StopListeningForNotifications"/>
+ /// <seealso cref="DeviceAvailable"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> ty is true.</exception>
+ public void StartListeningForNotifications()
+ {
+ ThrowIfDisposed();
+
+ _CommunicationsServer.RequestReceived -= CommsServer_RequestReceived;
+ _CommunicationsServer.RequestReceived += CommsServer_RequestReceived;
+ _CommunicationsServer.BeginListeningForBroadcasts();
+ }
+
+ /// <summary>
+ /// Stops listening for broadcast notifications of service availability.
+ /// </summary>
+ /// <remarks>
+ /// <para>Does nothing if this instance is not already listening for notifications.</para>
+ /// </remarks>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="DeviceAvailable"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception>
+ public void StopListeningForNotifications()
+ {
+ ThrowIfDisposed();
+
+ _CommunicationsServer.RequestReceived -= CommsServer_RequestReceived;
+ }
+
+ /// <summary>
+ /// Raises the <see cref="DeviceAvailable"/> event.
+ /// </summary>
+ /// <seealso cref="DeviceAvailable"/>
+ protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress)
+ {
+ if (this.IsDisposed) return;
+
+ var handlers = this.DeviceAvailable;
+ if (handlers != null)
+ handlers(this, new DeviceAvailableEventArgs(device, isNewDevice)
+ {
+ LocalIpAddress = localIpAddress
+ });
+ }
+
+ /// <summary>
+ /// Raises the <see cref="DeviceUnavailable"/> event.
+ /// </summary>
+ /// <param name="device">A <see cref="DiscoveredSsdpDevice"/> representing the device that is no longer available.</param>
+ /// <param name="expired">True if the device expired from the cache without being renewed, otherwise false to indicate the device explicitly notified us it was being shutdown.</param>
+ /// <seealso cref="DeviceUnavailable"/>
+ protected virtual void OnDeviceUnavailable(DiscoveredSsdpDevice device, bool expired)
+ {
+ if (this.IsDisposed) return;
+
+ var handlers = this.DeviceUnavailable;
+ if (handlers != null)
+ handlers(this, new DeviceUnavailableEventArgs(device, expired));
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="ISsdpDeviceLocator.DeviceAvailable"/> or <see cref="ISsdpDeviceLocator.DeviceUnavailable"/> events.
+ /// </summary>
+ /// <remarks>
+ /// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para>
+ /// <para>If the value is null or empty string then, all notifications are reported.</para>
+ /// <para>Example filters follow;</para>
+ /// <example>upnp:rootdevice</example>
+ /// <example>urn:schemas-upnp-org:device:WANDevice:1</example>
+ /// <example>uuid:9F15356CC-95FA-572E-0E99-85B456BD3012</example>
+ /// </remarks>
+ /// <seealso cref="ISsdpDeviceLocator.DeviceAvailable"/>
+ /// <seealso cref="ISsdpDeviceLocator.DeviceUnavailable"/>
+ /// <seealso cref="ISsdpDeviceLocator.StartListeningForNotifications"/>
+ /// <seealso cref="ISsdpDeviceLocator.StopListeningForNotifications"/>
+ public string NotificationFilter
+ {
+ get;
+ set;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ /// <summary>
+ /// Disposes this object and all internal resources. Stops listening for all network messages.
+ /// </summary>
+ /// <param name="disposing">True if managed resources should be disposed, or false is only unmanaged resources should be cleaned up.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ DisposeBroadcastTimer();
+
+ var commsServer = _CommunicationsServer;
+ _CommunicationsServer = null;
+ if (commsServer != null)
+ {
+ commsServer.ResponseReceived -= this.CommsServer_ResponseReceived;
+ commsServer.RequestReceived -= this.CommsServer_RequestReceived;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ #region Discovery/Device Add
+
+ private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IpAddressInfo localIpAddress)
+ {
+ bool isNewDevice = false;
+ lock (_Devices)
+ {
+ var existingDevice = FindExistingDeviceNotification(_Devices, device.NotificationType, device.Usn);
+ if (existingDevice == null)
+ {
+ _Devices.Add(device);
+ isNewDevice = true;
+ }
+ else
+ {
+ _Devices.Remove(existingDevice);
+ _Devices.Add(device);
+ }
+ }
+
+ DeviceFound(device, isNewDevice, localIpAddress);
+ }
+
+ private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress)
+ {
+ if (!NotificationTypeMatchesFilter(device)) return;
+
+ OnDeviceAvailable(device, isNewDevice, localIpAddress);
+ }
+
+ private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device)
+ {
+ return String.IsNullOrEmpty(this.NotificationFilter)
+ || this.NotificationFilter == SsdpConstants.SsdpDiscoverAllSTHeader
+ || device.NotificationType == this.NotificationFilter;
+ }
+
+ #endregion
+
+ #region Network Message Processing
+
+ private Task BroadcastDiscoverMessage(string serviceType, TimeSpan mxValue, CancellationToken cancellationToken)
+ {
+ var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ values["HOST"] = "239.255.255.250:1900";
+ values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2";
+ //values["X-EMBY-SERVERID"] = _appHost.SystemId;
+
+ values["MAN"] = "\"ssdp:discover\"";
+
+ // Search target
+ values["ST"] = "ssdp:all";
+
+ // Seconds to delay response
+ values["MX"] = "3";
+
+ var header = "M-SEARCH * HTTP/1.1";
+
+ var message = BuildMessage(header, values);
+
+ return _CommunicationsServer.SendMulticastMessage(message, cancellationToken);
+ }
+
+ private void ProcessSearchResponseMessage(HttpResponseMessage message, IpAddressInfo localIpAddress)
+ {
+ if (!message.IsSuccessStatusCode) return;
+
+ var location = GetFirstHeaderUriValue("Location", message);
+ if (location != null)
+ {
+ var device = new DiscoveredSsdpDevice()
+ {
+ DescriptionLocation = location,
+ Usn = GetFirstHeaderStringValue("USN", message),
+ NotificationType = GetFirstHeaderStringValue("ST", message),
+ CacheLifetime = CacheAgeFromHeader(message.Headers.CacheControl),
+ AsAt = DateTimeOffset.Now,
+ ResponseHeaders = message.Headers
+ };
+
+ AddOrUpdateDiscoveredDevice(device, localIpAddress);
+ }
+ }
+
+ private void ProcessNotificationMessage(HttpRequestMessage message, IpAddressInfo localIpAddress)
+ {
+ if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0) return;
+
+ var notificationType = GetFirstHeaderStringValue("NTS", message);
+ if (String.Compare(notificationType, SsdpConstants.SsdpKeepAliveNotification, StringComparison.OrdinalIgnoreCase) == 0)
+ ProcessAliveNotification(message, localIpAddress);
+ else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0)
+ ProcessByeByeNotification(message);
+ }
+
+ private void ProcessAliveNotification(HttpRequestMessage message, IpAddressInfo localIpAddress)
+ {
+ var location = GetFirstHeaderUriValue("Location", message);
+ if (location != null)
+ {
+ var device = new DiscoveredSsdpDevice()
+ {
+ DescriptionLocation = location,
+ Usn = GetFirstHeaderStringValue("USN", message),
+ NotificationType = GetFirstHeaderStringValue("NT", message),
+ CacheLifetime = CacheAgeFromHeader(message.Headers.CacheControl),
+ AsAt = DateTimeOffset.Now,
+ ResponseHeaders = message.Headers
+ };
+
+ AddOrUpdateDiscoveredDevice(device, localIpAddress);
+ }
+ }
+
+ private void ProcessByeByeNotification(HttpRequestMessage message)
+ {
+ var notficationType = GetFirstHeaderStringValue("NT", message);
+ if (!String.IsNullOrEmpty(notficationType))
+ {
+ var usn = GetFirstHeaderStringValue("USN", message);
+
+ if (!DeviceDied(usn, false))
+ {
+ var deadDevice = new DiscoveredSsdpDevice()
+ {
+ AsAt = DateTime.UtcNow,
+ CacheLifetime = TimeSpan.Zero,
+ DescriptionLocation = null,
+ NotificationType = GetFirstHeaderStringValue("NT", message),
+ Usn = usn,
+ ResponseHeaders = message.Headers
+ };
+
+ if (NotificationTypeMatchesFilter(deadDevice))
+ OnDeviceUnavailable(deadDevice, false);
+ }
+ }
+ }
+
+ #region Header/Message Processing Utilities
+
+ private string GetFirstHeaderStringValue(string headerName, HttpResponseMessage message)
+ {
+ string retVal = null;
+ IEnumerable<string> values;
+ if (message.Headers.Contains(headerName))
+ {
+ message.Headers.TryGetValues(headerName, out values);
+ if (values != null)
+ retVal = values.FirstOrDefault();
+ }
+
+ return retVal;
+ }
+
+ private string GetFirstHeaderStringValue(string headerName, HttpRequestMessage message)
+ {
+ string retVal = null;
+ IEnumerable<string> values;
+ if (message.Headers.Contains(headerName))
+ {
+ message.Headers.TryGetValues(headerName, out values);
+ if (values != null)
+ retVal = values.FirstOrDefault();
+ }
+
+ return retVal;
+ }
+
+ private Uri GetFirstHeaderUriValue(string headerName, HttpRequestMessage request)
+ {
+ string value = null;
+ IEnumerable<string> values;
+ if (request.Headers.Contains(headerName))
+ {
+ request.Headers.TryGetValues(headerName, out values);
+ if (values != null)
+ value = values.FirstOrDefault();
+ }
+
+ Uri retVal;
+ Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
+ return retVal;
+ }
+
+ private Uri GetFirstHeaderUriValue(string headerName, HttpResponseMessage response)
+ {
+ string value = null;
+ IEnumerable<string> values;
+ if (response.Headers.Contains(headerName))
+ {
+ response.Headers.TryGetValues(headerName, out values);
+ if (values != null)
+ value = values.FirstOrDefault();
+ }
+
+ Uri retVal;
+ Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
+ return retVal;
+ }
+
+ private TimeSpan CacheAgeFromHeader(System.Net.Http.Headers.CacheControlHeaderValue headerValue)
+ {
+ if (headerValue == null) return TimeSpan.Zero;
+
+ return (TimeSpan)(headerValue.MaxAge ?? headerValue.SharedMaxAge ?? TimeSpan.Zero);
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Expiry and Device Removal
+
+ private void RemoveExpiredDevicesFromCache()
+ {
+ if (this.IsDisposed) return;
+
+ DiscoveredSsdpDevice[] expiredDevices = null;
+ lock (_Devices)
+ {
+ expiredDevices = (from device in _Devices where device.IsExpired() select device).ToArray();
+
+ foreach (var device in expiredDevices)
+ {
+ if (this.IsDisposed) return;
+
+ _Devices.Remove(device);
+ }
+ }
+
+ // Don't do this inside lock because DeviceDied raises an event
+ // which means public code may execute during lock and cause
+ // problems.
+ foreach (var expiredUsn in (from expiredDevice in expiredDevices select expiredDevice.Usn).Distinct())
+ {
+ if (this.IsDisposed) return;
+
+ DeviceDied(expiredUsn, true);
+ }
+ }
+
+ private bool DeviceDied(string deviceUsn, bool expired)
+ {
+ List<DiscoveredSsdpDevice> existingDevices = null;
+ lock (_Devices)
+ {
+ existingDevices = FindExistingDeviceNotifications(_Devices, deviceUsn);
+ foreach (var existingDevice in existingDevices)
+ {
+ if (this.IsDisposed) return true;
+
+ _Devices.Remove(existingDevice);
+ }
+ }
+
+ if (existingDevices != null && existingDevices.Count > 0)
+ {
+ foreach (var removedDevice in existingDevices)
+ {
+ if (NotificationTypeMatchesFilter(removedDevice))
+ OnDeviceUnavailable(removedDevice, expired);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ private TimeSpan SearchTimeToMXValue(TimeSpan searchWaitTime)
+ {
+ if (searchWaitTime.TotalSeconds < 2 || searchWaitTime == TimeSpan.Zero)
+ return OneSecond;
+ else
+ return searchWaitTime.Subtract(OneSecond);
+ }
+
+ private DiscoveredSsdpDevice FindExistingDeviceNotification(IEnumerable<DiscoveredSsdpDevice> devices, string notificationType, string usn)
+ {
+ foreach (var d in devices)
+ {
+ if (d.NotificationType == notificationType && d.Usn == usn)
+ {
+ return d;
+ }
+ }
+ return null;
+ }
+
+ private List<DiscoveredSsdpDevice> FindExistingDeviceNotifications(IList<DiscoveredSsdpDevice> devices, string usn)
+ {
+ var list = new List<DiscoveredSsdpDevice>();
+
+ foreach (var d in devices)
+ {
+ if (d.Usn == usn)
+ {
+ list.Add(d);
+ }
+ }
+
+ return list;
+ }
+
+ #endregion
+
+ #region Event Handlers
+
+ private void CommsServer_ResponseReceived(object sender, ResponseReceivedEventArgs e)
+ {
+ ProcessSearchResponseMessage(e.Message, e.LocalIpAddress);
+ }
+
+ private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
+ {
+ ProcessNotificationMessage(e.Message, e.LocalIpAddress);
+ }
+
+ #endregion
+
+ }
} \ No newline at end of file
diff --git a/RSSDP/SsdpDeviceLocatorBase.cs b/RSSDP/SsdpDeviceLocatorBase.cs
deleted file mode 100644
index d1eaef88a..000000000
--- a/RSSDP/SsdpDeviceLocatorBase.cs
+++ /dev/null
@@ -1,628 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Threading;
-using RSSDP;
-
-namespace Rssdp.Infrastructure
-{
- /// <summary>
- /// Allows you to search the network for a particular device, device types, or UPnP service types. Also listenings for broadcast notifications of device availability and raises events to indicate changes in status.
- /// </summary>
- public abstract class SsdpDeviceLocatorBase : DisposableManagedObjectBase
- {
-
- #region Fields & Constants
-
- private List<DiscoveredSsdpDevice> _Devices;
- private ISsdpCommunicationsServer _CommunicationsServer;
-
- private ITimer _BroadcastTimer;
- private ITimerFactory _timerFactory;
- private object _timerLock = new object();
-
- private static readonly TimeSpan DefaultSearchWaitTime = TimeSpan.FromSeconds(4);
- private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- protected SsdpDeviceLocatorBase(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory)
- {
- if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
-
- _CommunicationsServer = communicationsServer;
- _timerFactory = timerFactory;
- _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived;
-
- _Devices = new List<DiscoveredSsdpDevice>();
- }
-
- #endregion
-
- #region Events
-
- /// <summary>
- /// Raised for when
- /// <list type="bullet">
- /// <item>An 'alive' notification is received that a device, regardless of whether or not that device is not already in the cache or has previously raised this event.</item>
- /// <item>For each item found during a device <see cref="SearchAsync()"/> (cached or not), allowing clients to respond to found devices before the entire search is complete.</item>
- /// <item>Only if the notification type matches the <see cref="NotificationFilter"/> property. By default the filter is null, meaning all notifications raise events (regardless of ant </item>
- /// </list>
- /// <para>This event may be raised from a background thread, if interacting with UI or other objects with specific thread affinity invoking to the relevant thread is required.</para>
- /// </summary>
- /// <seealso cref="NotificationFilter"/>
- /// <seealso cref="DeviceUnavailable"/>
- /// <seealso cref="StartListeningForNotifications"/>
- /// <seealso cref="StopListeningForNotifications"/>
- public event EventHandler<DeviceAvailableEventArgs> DeviceAvailable;
-
- /// <summary>
- /// Raised when a notification is received that indicates a device has shutdown or otherwise become unavailable.
- /// </summary>
- /// <remarks>
- /// <para>Devices *should* broadcast these types of notifications, but not all devices do and sometimes (in the event of power loss for example) it might not be possible for a device to do so. You should also implement error handling when trying to contact a device, even if RSSDP is reporting that device as available.</para>
- /// <para>This event is only raised if the notification type matches the <see cref="NotificationFilter"/> property. A null or empty string for the <see cref="NotificationFilter"/> will be treated as no filter and raise the event for all notifications.</para>
- /// <para>The <see cref="DeviceUnavailableEventArgs.DiscoveredDevice"/> property may contain either a fully complete <see cref="DiscoveredSsdpDevice"/> instance, or one containing just a USN and NotificationType property. Full information is available if the device was previously discovered and cached, but only partial information if a byebye notification was received for a previously unseen or expired device.</para>
- /// <para>This event may be raised from a background thread, if interacting with UI or other objects with specific thread affinity invoking to the relevant thread is required.</para>
- /// </remarks>
- /// <seealso cref="NotificationFilter"/>
- /// <seealso cref="DeviceAvailable"/>
- /// <seealso cref="StartListeningForNotifications"/>
- /// <seealso cref="StopListeningForNotifications"/>
- public event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable;
-
- #endregion
-
- #region Public Methods
-
- #region Search Overloads
-
- public void RestartBroadcastTimer(TimeSpan dueTime, TimeSpan period)
- {
- lock (_timerLock)
- {
- if (_BroadcastTimer == null)
- {
- _BroadcastTimer = _timerFactory.Create(OnBroadcastTimerCallback, null, dueTime, period);
- }
- else
- {
- _BroadcastTimer.Change(dueTime, period);
- }
- }
- }
-
- public void DisposeBroadcastTimer()
- {
- lock (_timerLock)
- {
- if (_BroadcastTimer != null)
- {
- _BroadcastTimer.Dispose();
- _BroadcastTimer = null;
- }
- }
- }
-
- private async void OnBroadcastTimerCallback(object state)
- {
- StartListeningForNotifications();
- RemoveExpiredDevicesFromCache();
-
- try
- {
- await SearchAsync(CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
-
- }
- }
-
- /// <summary>
- /// Performs a search for all devices using the default search timeout.
- /// </summary>
- private Task SearchAsync(CancellationToken cancellationToken)
- {
- return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, DefaultSearchWaitTime, cancellationToken);
- }
-
- /// <summary>
- /// Performs a search for the specified search target (criteria) and default search timeout.
- /// </summary>
- /// <param name="searchTarget">The criteria for the search. Value can be;
- /// <list type="table">
- /// <item><term>Root devices</term><description>upnp:rootdevice</description></item>
- /// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item>
- /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item>
- /// </list>
- /// </param>
- private Task SearchAsync(string searchTarget)
- {
- return SearchAsync(searchTarget, DefaultSearchWaitTime, CancellationToken.None);
- }
-
- /// <summary>
- /// Performs a search for all devices using the specified search timeout.
- /// </summary>
- /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 seconds is recommended by the UPnP 1.1 specification, this method requires the value be greater 1 second if it is not zero. Specify TimeSpan.Zero to return only devices already in the cache.</param>
- private Task SearchAsync(TimeSpan searchWaitTime)
- {
- return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, searchWaitTime, CancellationToken.None);
- }
-
- private Task SearchAsync(string searchTarget, TimeSpan searchWaitTime, CancellationToken cancellationToken)
- {
- if (searchTarget == null) throw new ArgumentNullException("searchTarget");
- if (searchTarget.Length == 0) throw new ArgumentException("searchTarget cannot be an empty string.", "searchTarget");
- if (searchWaitTime.TotalSeconds < 0) throw new ArgumentException("searchWaitTime must be a positive time.");
- if (searchWaitTime.TotalSeconds > 0 && searchWaitTime.TotalSeconds <= 1) throw new ArgumentException("searchWaitTime must be zero (if you are not using the result and relying entirely in the events), or greater than one second.");
-
- ThrowIfDisposed();
-
- return BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime), cancellationToken);
- }
-
- #endregion
-
- /// <summary>
- /// Starts listening for broadcast notifications of service availability.
- /// </summary>
- /// <remarks>
- /// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para>
- /// </remarks>
- /// <seealso cref="StopListeningForNotifications"/>
- /// <seealso cref="DeviceAvailable"/>
- /// <seealso cref="DeviceUnavailable"/>
- /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> ty is true.</exception>
- public void StartListeningForNotifications()
- {
- ThrowIfDisposed();
-
- _CommunicationsServer.RequestReceived -= CommsServer_RequestReceived;
- _CommunicationsServer.RequestReceived += CommsServer_RequestReceived;
- _CommunicationsServer.BeginListeningForBroadcasts();
- }
-
- /// <summary>
- /// Stops listening for broadcast notifications of service availability.
- /// </summary>
- /// <remarks>
- /// <para>Does nothing if this instance is not already listening for notifications.</para>
- /// </remarks>
- /// <seealso cref="StartListeningForNotifications"/>
- /// <seealso cref="DeviceAvailable"/>
- /// <seealso cref="DeviceUnavailable"/>
- /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception>
- public void StopListeningForNotifications()
- {
- ThrowIfDisposed();
-
- _CommunicationsServer.RequestReceived -= CommsServer_RequestReceived;
- }
-
- /// <summary>
- /// Raises the <see cref="DeviceAvailable"/> event.
- /// </summary>
- /// <seealso cref="DeviceAvailable"/>
- protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress)
- {
- if (this.IsDisposed) return;
-
- var handlers = this.DeviceAvailable;
- if (handlers != null)
- handlers(this, new DeviceAvailableEventArgs(device, isNewDevice)
- {
- LocalIpAddress = localIpAddress
- });
- }
-
- /// <summary>
- /// Raises the <see cref="DeviceUnavailable"/> event.
- /// </summary>
- /// <param name="device">A <see cref="DiscoveredSsdpDevice"/> representing the device that is no longer available.</param>
- /// <param name="expired">True if the device expired from the cache without being renewed, otherwise false to indicate the device explicitly notified us it was being shutdown.</param>
- /// <seealso cref="DeviceUnavailable"/>
- protected virtual void OnDeviceUnavailable(DiscoveredSsdpDevice device, bool expired)
- {
- if (this.IsDisposed) return;
-
- var handlers = this.DeviceUnavailable;
- if (handlers != null)
- handlers(this, new DeviceUnavailableEventArgs(device, expired));
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="ISsdpDeviceLocator.DeviceAvailable"/> or <see cref="ISsdpDeviceLocator.DeviceUnavailable"/> events.
- /// </summary>
- /// <remarks>
- /// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para>
- /// <para>If the value is null or empty string then, all notifications are reported.</para>
- /// <para>Example filters follow;</para>
- /// <example>upnp:rootdevice</example>
- /// <example>urn:schemas-upnp-org:device:WANDevice:1</example>
- /// <example>uuid:9F15356CC-95FA-572E-0E99-85B456BD3012</example>
- /// </remarks>
- /// <seealso cref="ISsdpDeviceLocator.DeviceAvailable"/>
- /// <seealso cref="ISsdpDeviceLocator.DeviceUnavailable"/>
- /// <seealso cref="ISsdpDeviceLocator.StartListeningForNotifications"/>
- /// <seealso cref="ISsdpDeviceLocator.StopListeningForNotifications"/>
- public string NotificationFilter
- {
- get;
- set;
- }
-
- #endregion
-
- #region Overrides
-
- /// <summary>
- /// Disposes this object and all internal resources. Stops listening for all network messages.
- /// </summary>
- /// <param name="disposing">True if managed resources should be disposed, or false is only unmanaged resources should be cleaned up.</param>
- protected override void Dispose(bool disposing)
- {
-
- if (disposing)
- {
- DisposeBroadcastTimer();
-
- var commsServer = _CommunicationsServer;
- _CommunicationsServer = null;
- if (commsServer != null)
- {
- commsServer.ResponseReceived -= this.CommsServer_ResponseReceived;
- commsServer.RequestReceived -= this.CommsServer_RequestReceived;
- if (!commsServer.IsShared)
- commsServer.Dispose();
- }
- }
- }
-
- #endregion
-
- #region Private Methods
-
- #region Discovery/Device Add
-
- private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IpAddressInfo localIpAddress)
- {
- bool isNewDevice = false;
- lock (_Devices)
- {
- var existingDevice = FindExistingDeviceNotification(_Devices, device.NotificationType, device.Usn);
- if (existingDevice == null)
- {
- _Devices.Add(device);
- isNewDevice = true;
- }
- else
- {
- _Devices.Remove(existingDevice);
- _Devices.Add(device);
- }
- }
-
- DeviceFound(device, isNewDevice, localIpAddress);
- }
-
- private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress)
- {
- if (!NotificationTypeMatchesFilter(device)) return;
-
- OnDeviceAvailable(device, isNewDevice, localIpAddress);
- }
-
- private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device)
- {
- return String.IsNullOrEmpty(this.NotificationFilter)
- || this.NotificationFilter == SsdpConstants.SsdpDiscoverAllSTHeader
- || device.NotificationType == this.NotificationFilter;
- }
-
- #endregion
-
- #region Network Message Processing
-
- private Task BroadcastDiscoverMessage(string serviceType, TimeSpan mxValue, CancellationToken cancellationToken)
- {
- var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- values["HOST"] = "239.255.255.250:1900";
- values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2";
- //values["X-EMBY-SERVERID"] = _appHost.SystemId;
-
- values["MAN"] = "\"ssdp:discover\"";
-
- // Search target
- values["ST"] = "ssdp:all";
-
- // Seconds to delay response
- values["MX"] = "3";
-
- var header = "M-SEARCH * HTTP/1.1";
-
- var message = SsdpHelper.BuildMessage(header, values);
-
- return _CommunicationsServer.SendMulticastMessage(message, cancellationToken);
- }
-
- private void ProcessSearchResponseMessage(HttpResponseMessage message, IpAddressInfo localIpAddress)
- {
- if (!message.IsSuccessStatusCode) return;
-
- var location = GetFirstHeaderUriValue("Location", message);
- if (location != null)
- {
- var device = new DiscoveredSsdpDevice()
- {
- DescriptionLocation = location,
- Usn = GetFirstHeaderStringValue("USN", message),
- NotificationType = GetFirstHeaderStringValue("ST", message),
- CacheLifetime = CacheAgeFromHeader(message.Headers.CacheControl),
- AsAt = DateTimeOffset.Now,
- ResponseHeaders = message.Headers
- };
-
- AddOrUpdateDiscoveredDevice(device, localIpAddress);
- }
- }
-
- private void ProcessNotificationMessage(HttpRequestMessage message, IpAddressInfo localIpAddress)
- {
- if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0) return;
-
- var notificationType = GetFirstHeaderStringValue("NTS", message);
- if (String.Compare(notificationType, SsdpConstants.SsdpKeepAliveNotification, StringComparison.OrdinalIgnoreCase) == 0)
- ProcessAliveNotification(message, localIpAddress);
- else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0)
- ProcessByeByeNotification(message);
- }
-
- private void ProcessAliveNotification(HttpRequestMessage message, IpAddressInfo localIpAddress)
- {
- var location = GetFirstHeaderUriValue("Location", message);
- if (location != null)
- {
- var device = new DiscoveredSsdpDevice()
- {
- DescriptionLocation = location,
- Usn = GetFirstHeaderStringValue("USN", message),
- NotificationType = GetFirstHeaderStringValue("NT", message),
- CacheLifetime = CacheAgeFromHeader(message.Headers.CacheControl),
- AsAt = DateTimeOffset.Now,
- ResponseHeaders = message.Headers
- };
-
- AddOrUpdateDiscoveredDevice(device, localIpAddress);
- }
- }
-
- private void ProcessByeByeNotification(HttpRequestMessage message)
- {
- var notficationType = GetFirstHeaderStringValue("NT", message);
- if (!String.IsNullOrEmpty(notficationType))
- {
- var usn = GetFirstHeaderStringValue("USN", message);
-
- if (!DeviceDied(usn, false))
- {
- var deadDevice = new DiscoveredSsdpDevice()
- {
- AsAt = DateTime.UtcNow,
- CacheLifetime = TimeSpan.Zero,
- DescriptionLocation = null,
- NotificationType = GetFirstHeaderStringValue("NT", message),
- Usn = usn,
- ResponseHeaders = message.Headers
- };
-
- if (NotificationTypeMatchesFilter(deadDevice))
- OnDeviceUnavailable(deadDevice, false);
- }
- }
- }
-
- #region Header/Message Processing Utilities
-
- private static string GetFirstHeaderStringValue(string headerName, HttpResponseMessage message)
- {
- string retVal = null;
- IEnumerable<string> values;
- if (message.Headers.Contains(headerName))
- {
- message.Headers.TryGetValues(headerName, out values);
- if (values != null)
- retVal = values.FirstOrDefault();
- }
-
- return retVal;
- }
-
- private static string GetFirstHeaderStringValue(string headerName, HttpRequestMessage message)
- {
- string retVal = null;
- IEnumerable<string> values;
- if (message.Headers.Contains(headerName))
- {
- message.Headers.TryGetValues(headerName, out values);
- if (values != null)
- retVal = values.FirstOrDefault();
- }
-
- return retVal;
- }
-
- private static Uri GetFirstHeaderUriValue(string headerName, HttpRequestMessage request)
- {
- string value = null;
- IEnumerable<string> values;
- if (request.Headers.Contains(headerName))
- {
- request.Headers.TryGetValues(headerName, out values);
- if (values != null)
- value = values.FirstOrDefault();
- }
-
- Uri retVal;
- Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
- return retVal;
- }
-
- private static Uri GetFirstHeaderUriValue(string headerName, HttpResponseMessage response)
- {
- string value = null;
- IEnumerable<string> values;
- if (response.Headers.Contains(headerName))
- {
- response.Headers.TryGetValues(headerName, out values);
- if (values != null)
- value = values.FirstOrDefault();
- }
-
- Uri retVal;
- Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal);
- return retVal;
- }
-
- private static TimeSpan CacheAgeFromHeader(System.Net.Http.Headers.CacheControlHeaderValue headerValue)
- {
- if (headerValue == null) return TimeSpan.Zero;
-
- return (TimeSpan)(headerValue.MaxAge ?? headerValue.SharedMaxAge ?? TimeSpan.Zero);
- }
-
- #endregion
-
- #endregion
-
- #region Expiry and Device Removal
-
- private void RemoveExpiredDevicesFromCache()
- {
- if (this.IsDisposed) return;
-
- DiscoveredSsdpDevice[] expiredDevices = null;
- lock (_Devices)
- {
- expiredDevices = (from device in _Devices where device.IsExpired() select device).ToArray();
-
- foreach (var device in expiredDevices)
- {
- if (this.IsDisposed) return;
-
- _Devices.Remove(device);
- }
- }
-
- // Don't do this inside lock because DeviceDied raises an event
- // which means public code may execute during lock and cause
- // problems.
- foreach (var expiredUsn in (from expiredDevice in expiredDevices select expiredDevice.Usn).Distinct())
- {
- if (this.IsDisposed) return;
-
- DeviceDied(expiredUsn, true);
- }
- }
-
- private bool DeviceDied(string deviceUsn, bool expired)
- {
- List<DiscoveredSsdpDevice> existingDevices = null;
- lock (_Devices)
- {
- existingDevices = FindExistingDeviceNotifications(_Devices, deviceUsn);
- foreach (var existingDevice in existingDevices)
- {
- if (this.IsDisposed) return true;
-
- _Devices.Remove(existingDevice);
- }
- }
-
- if (existingDevices != null && existingDevices.Count > 0)
- {
- foreach (var removedDevice in existingDevices)
- {
- if (NotificationTypeMatchesFilter(removedDevice))
- OnDeviceUnavailable(removedDevice, expired);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- private static TimeSpan SearchTimeToMXValue(TimeSpan searchWaitTime)
- {
- if (searchWaitTime.TotalSeconds < 2 || searchWaitTime == TimeSpan.Zero)
- return OneSecond;
- else
- return searchWaitTime.Subtract(OneSecond);
- }
-
- private static DiscoveredSsdpDevice FindExistingDeviceNotification(IEnumerable<DiscoveredSsdpDevice> devices, string notificationType, string usn)
- {
- foreach (var d in devices)
- {
- if (d.NotificationType == notificationType && d.Usn == usn)
- {
- return d;
- }
- }
- return null;
- }
-
- private static List<DiscoveredSsdpDevice> FindExistingDeviceNotifications(IList<DiscoveredSsdpDevice> devices, string usn)
- {
- var list = new List<DiscoveredSsdpDevice>();
-
- foreach (var d in devices)
- {
- if (d.Usn == usn)
- {
- list.Add(d);
- }
- }
-
- return list;
- }
-
- #endregion
-
- #region Event Handlers
-
- private void CommsServer_ResponseReceived(object sender, ResponseReceivedEventArgs e)
- {
- ProcessSearchResponseMessage(e.Message, e.LocalIpAddress);
- }
-
- private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
- {
- ProcessNotificationMessage(e.Message, e.LocalIpAddress);
- }
-
- #endregion
-
- }
-} \ No newline at end of file
diff --git a/RSSDP/SsdpDeviceProperties.cs b/RSSDP/SsdpDeviceProperties.cs
deleted file mode 100644
index ae5309da5..000000000
--- a/RSSDP/SsdpDeviceProperties.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Rssdp
-{
- /// <summary>
- /// Represents a collection of <see cref="SsdpDeviceProperty"/> instances keyed by the <see cref="SsdpDeviceProperty.FullName"/> property value.
- /// </summary>
- /// <remarks>
- /// <para>Items added to this collection are keyed by their <see cref="SsdpDeviceProperty.FullName"/> property value, at the time they were added. If the name changes after they were added to the collection, the key is not updated unless the item is manually removed and re-added to the collection.</para>
- /// </remarks>
- public class SsdpDevicePropertiesCollection : IEnumerable<SsdpDeviceProperty>
- {
-
- #region Fields
-
- private IDictionary<string, SsdpDeviceProperty> _Properties;
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- public SsdpDevicePropertiesCollection()
- {
- _Properties = new Dictionary<string, SsdpDeviceProperty>();
- }
-
- /// <summary>
- /// Full constructor.
- /// </summary>
- /// <param name="capacity">Specifies the initial capacity of the collection.</param>
- public SsdpDevicePropertiesCollection(int capacity)
- {
- _Properties = new Dictionary<string, SsdpDeviceProperty>(capacity);
- }
-
- #endregion
-
- #region Public Methpds
-
- /// <summary>
- /// Adds a <see cref="SsdpDeviceProperty"/> instance to the collection.
- /// </summary>
- /// <param name="customDeviceProperty">The property instance to add to the collection.</param>
- /// <remarks>
- /// <para></para>
- /// </remarks>
- /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="customDeviceProperty"/> is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the <see cref="SsdpDeviceProperty.FullName"/> property of the <paramref name="customDeviceProperty"/> argument is null or empty string, or if the collection already contains an item with the same key.</exception>
- public void Add(SsdpDeviceProperty customDeviceProperty)
- {
- if (customDeviceProperty == null) throw new ArgumentNullException("customDeviceProperty");
- if (String.IsNullOrEmpty(customDeviceProperty.FullName)) throw new ArgumentException("customDeviceProperty.FullName cannot be null or empty.");
-
- lock (_Properties)
- {
- _Properties.Add(customDeviceProperty.FullName, customDeviceProperty);
- }
- }
-
- #region Remove Overloads
-
- /// <summary>
- /// Removes the specified property instance from the collection.
- /// </summary>
- /// <param name="customDeviceProperty">The <see cref="SsdpDeviceProperty"/> instance to remove from the collection.</param>
- /// <remarks>
- /// <para>Only remove the specified property if that instance was in the collection, if another property with the same full name exists in the collection it is not removed.</para>
- /// </remarks>
- /// <returns>True if an item was removed from the collection, otherwise false (because it did not exist or was not the same instance).</returns>
- /// <seealso cref="Remove(string)"/>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="customDeviceProperty"/> is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the <see cref="SsdpDeviceProperty.FullName"/> property of the <paramref name="customDeviceProperty"/> argument is null or empty string, or if the collection already contains an item with the same key.</exception>
- public bool Remove(SsdpDeviceProperty customDeviceProperty)
- {
- if (customDeviceProperty == null) throw new ArgumentNullException("customDeviceProperty");
- if (String.IsNullOrEmpty(customDeviceProperty.FullName)) throw new ArgumentException("customDeviceProperty.FullName cannot be null or empty.");
-
- lock (_Properties)
- {
- if (_Properties.ContainsKey(customDeviceProperty.FullName) && _Properties[customDeviceProperty.FullName] == customDeviceProperty)
- return _Properties.Remove(customDeviceProperty.FullName);
- }
-
- return false;
- }
-
- /// <summary>
- /// Removes the property with the specified key (<see cref="SsdpDeviceProperty.FullName"/> from the collection.
- /// </summary>
- /// <param name="customDevicePropertyFullName">The full name of the <see cref="SsdpDeviceProperty"/> instance to remove from the collection.</param>
- /// <returns>True if an item was removed from the collection, otherwise false (because no item exists in the collection with that key).</returns>
- /// <exception cref="System.ArgumentException">Thrown if the <paramref name="customDevicePropertyFullName"/> argument is null or empty string.</exception>
- public bool Remove(string customDevicePropertyFullName)
- {
- if (String.IsNullOrEmpty(customDevicePropertyFullName)) throw new ArgumentException("customDevicePropertyFullName cannot be null or empty.");
-
- lock (_Properties)
- {
- return _Properties.Remove(customDevicePropertyFullName);
- }
- }
-
- #endregion
-
- /// <summary>
- /// Returns a boolean indicating whether or not the specified <see cref="SsdpDeviceProperty"/> instance is in the collection.
- /// </summary>
- /// <param name="customDeviceProperty">An <see cref="SsdpDeviceProperty"/> instance to check the collection for.</param>
- /// <returns>True if the specified instance exists in the collection, otherwise false.</returns>
- public bool Contains(SsdpDeviceProperty customDeviceProperty)
- {
- if (customDeviceProperty == null) throw new ArgumentNullException("customDeviceProperty");
- if (String.IsNullOrEmpty(customDeviceProperty.FullName)) throw new ArgumentException("customDeviceProperty.FullName cannot be null or empty.");
-
- lock (_Properties)
- {
- if (_Properties.ContainsKey(customDeviceProperty.FullName))
- return _Properties[customDeviceProperty.FullName] == customDeviceProperty;
- }
-
- return false;
- }
-
- /// <summary>
- /// Returns a boolean indicating whether or not a <see cref="SsdpDeviceProperty"/> instance with the specified full name value exists in the collection.
- /// </summary>
- /// <param name="customDevicePropertyFullName">A string containing the full name of the <see cref="SsdpDeviceProperty"/> instance to check for.</param>
- /// <returns>True if an item with the specified full name exists in the collection, otherwise false.</returns>
- public bool Contains(string customDevicePropertyFullName)
- {
- if (String.IsNullOrEmpty(customDevicePropertyFullName)) throw new ArgumentException("customDevicePropertyFullName cannot be null or empty.");
-
- lock (_Properties)
- {
- return _Properties.ContainsKey(customDevicePropertyFullName);
- }
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Returns the number of items in the collection.
- /// </summary>
- public int Count
- {
- get { return _Properties.Count; }
- }
-
- /// <summary>
- /// Returns the <see cref="SsdpDeviceProperty"/> instance from the collection that has the specified <see cref="SsdpDeviceProperty.FullName"/> value.
- /// </summary>
- /// <param name="fullName">The full name of the property to return.</param>
- /// <returns>A <see cref="SsdpDeviceProperty"/> instance from the collection.</returns>
- /// <exception cref="System.Collections.Generic.KeyNotFoundException">Thrown if no item exists in the collection with the specified <paramref name="fullName"/> value.</exception>
- public SsdpDeviceProperty this[string fullName]
- {
- get
- {
- return _Properties[fullName];
- }
- }
-
- #endregion
-
- #region IEnumerable<SsdpDeviceProperty> Members
-
- /// <summary>
- /// Returns an enumerator of <see cref="SsdpDeviceProperty"/> instances in this collection.
- /// </summary>
- /// <returns>An enumerator of <see cref="SsdpDeviceProperty"/> instances in this collection.</returns>
- public IEnumerator<SsdpDeviceProperty> GetEnumerator()
- {
- lock (_Properties)
- {
- return _Properties.Values.GetEnumerator();
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- /// <summary>
- /// Returns an enumerator of <see cref="SsdpDeviceProperty"/> instances in this collection.
- /// </summary>
- /// <returns>An enumerator of <see cref="SsdpDeviceProperty"/> instances in this collection.</returns>
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- lock (_Properties)
- {
- return _Properties.Values.GetEnumerator();
- }
- }
-
- #endregion
-
- }
-} \ No newline at end of file
diff --git a/RSSDP/SsdpDeviceProperty.cs b/RSSDP/SsdpDeviceProperty.cs
deleted file mode 100644
index 3abcfb9aa..000000000
--- a/RSSDP/SsdpDeviceProperty.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Rssdp
-{
- /// <summary>
- /// Represents a custom property of an <see cref="SsdpDevice"/>.
- /// </summary>
- public sealed class SsdpDeviceProperty
- {
-
- /// <summary>
- /// Sets or returns the namespace this property exists in.
- /// </summary>
- public string Namespace { get; set; }
-
- /// <summary>
- /// Sets or returns the name of this property.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Returns the full name of this property (namespace and name).
- /// </summary>
- public string FullName { get { return String.IsNullOrEmpty(this.Namespace) ? this.Name : this.Namespace + ":" + this.Name; } }
-
- /// <summary>
- /// Sets or returns the value of this property.
- /// </summary>
- public string Value { get; set; }
-
- }
-}
diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs
index 2aa143775..8d57deb5e 100644
--- a/RSSDP/SsdpDevicePublisher.cs
+++ b/RSSDP/SsdpDevicePublisher.cs
@@ -1,37 +1,561 @@
-using System;
+using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Net.Http;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
-using Rssdp.Infrastructure;
+using Rssdp;
-namespace Rssdp
+namespace Rssdp.Infrastructure
{
- /// <summary>
- /// Allows publishing devices both as notification and responses to search requests.
- /// </summary>
- /// <remarks>
- /// This is the 'server' part of the system. You add your devices to an instance of this class so clients can find them.
- /// </remarks>
- public class SsdpDevicePublisher : SsdpDevicePublisherBase
- {
-
- #region Constructors
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- /// <remarks>
- /// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification.</para>
- /// </remarks>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")]
- public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory, string osName, string osVersion)
- : base(communicationsServer, timerFactory, osName, osVersion)
- {
-
- }
-
- #endregion
+ /// <summary>
+ /// Provides the platform independent logic for publishing SSDP devices (notifications and search responses).
+ /// </summary>
+ public class SsdpDevicePublisher : DisposableManagedObjectBase, ISsdpDevicePublisher
+ {
+ private ISsdpCommunicationsServer _CommsServer;
+ private string _OSName;
+ private string _OSVersion;
+
+ private bool _SupportPnpRootDevice;
+
+ private IList<SsdpRootDevice> _Devices;
+ private IReadOnlyList<SsdpRootDevice> _ReadOnlyDevices;
+
+ private ITimer _RebroadcastAliveNotificationsTimer;
+ private ITimerFactory _timerFactory;
+
+ private IDictionary<string, SearchRequest> _RecentSearchRequests;
+
+ private Random _Random;
+
+ private const string ServerVersion = "1.0";
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory, string osName, string osVersion)
+ {
+ if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
+ if (osName == null) throw new ArgumentNullException("osName");
+ if (osName.Length == 0) throw new ArgumentException("osName cannot be an empty string.", "osName");
+ if (osVersion == null) throw new ArgumentNullException("osVersion");
+ if (osVersion.Length == 0) throw new ArgumentException("osVersion cannot be an empty string.", "osName");
+
+ _SupportPnpRootDevice = true;
+ _timerFactory = timerFactory;
+ _Devices = new List<SsdpRootDevice>();
+ _ReadOnlyDevices = new ReadOnlyCollection<SsdpRootDevice>(_Devices);
+ _RecentSearchRequests = new Dictionary<string, SearchRequest>(StringComparer.OrdinalIgnoreCase);
+ _Random = new Random();
+
+ _CommsServer = communicationsServer;
+ _CommsServer.RequestReceived += CommsServer_RequestReceived;
+ _OSName = osName;
+ _OSVersion = osVersion;
+
+ _CommsServer.BeginListeningForBroadcasts();
+ }
+
+ public void StartBroadcastingAliveMessages(TimeSpan interval)
+ {
+ _RebroadcastAliveNotificationsTimer = _timerFactory.Create(SendAllAliveNotifications, null, TimeSpan.FromSeconds(5), interval);
+ }
+
+ /// <summary>
+ /// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients.
+ /// </summary>
+ /// <remarks>
+ /// <para>Adding a device causes "alive" notification messages to be sent immediately, or very soon after. Ensure your device/description service is running before adding the device object here.</para>
+ /// <para>Devices added here with a non-zero cache life time will also have notifications broadcast periodically.</para>
+ /// <para>This method ignores duplicate device adds (if the same device instance is added multiple times, the second and subsequent add calls do nothing).</para>
+ /// </remarks>
+ /// <param name="device">The <see cref="SsdpDevice"/> instance to add.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
+ /// <exception cref="System.InvalidOperationException">Thrown if the <paramref name="device"/> contains property values that are not acceptable to the UPnP 1.0 specification.</exception>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable supresses compiler warning, but task is not really needed.")]
+ public void AddDevice(SsdpRootDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+
+ ThrowIfDisposed();
+
+ TimeSpan minCacheTime = TimeSpan.Zero;
+ bool wasAdded = false;
+ lock (_Devices)
+ {
+ if (!_Devices.Contains(device))
+ {
+ _Devices.Add(device);
+ wasAdded = true;
+ minCacheTime = GetMinimumNonZeroCacheLifetime();
+ }
+ }
+
+ if (wasAdded)
+ {
+ WriteTrace("Device Added", device);
+
+ SendAliveNotifications(device, true, CancellationToken.None);
+ }
+ }
+
+ /// <summary>
+ /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable.
+ /// </summary>
+ /// <remarks>
+ /// <para>Removing a device causes "byebye" notification messages to be sent immediately, advising clients of the device/service becoming unavailable. We recommend removing the device from the published list before shutting down the actual device/service, if possible.</para>
+ /// <para>This method does nothing if the device was not found in the collection.</para>
+ /// </remarks>
+ /// <param name="device">The <see cref="SsdpDevice"/> instance to add.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
+ public async Task RemoveDevice(SsdpRootDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+
+ bool wasRemoved = false;
+ TimeSpan minCacheTime = TimeSpan.Zero;
+ lock (_Devices)
+ {
+ if (_Devices.Contains(device))
+ {
+ _Devices.Remove(device);
+ wasRemoved = true;
+ minCacheTime = GetMinimumNonZeroCacheLifetime();
+ }
+ }
+
+ if (wasRemoved)
+ {
+ WriteTrace("Device Removed", device);
+
+ await SendByeByeNotifications(device, true, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+
+ /// <summary>
+ /// Returns a read only list of devices being published by this instance.
+ /// </summary>
+ public IEnumerable<SsdpRootDevice> Devices
+ {
+ get
+ {
+ return _ReadOnlyDevices;
+ }
+ }
+
+ /// <summary>
+ /// If true (default) treats root devices as both upnp:rootdevice and pnp:rootdevice types.
+ /// </summary>
+ /// <remarks>
+ /// <para>Enabling this option will cause devices to show up in Microsoft Windows Explorer's network screens (if discovery is enabled etc.). Windows Explorer appears to search only for pnp:rootdeivce and not upnp:rootdevice.</para>
+ /// <para>If false, the system will only use upnp:rootdevice for notifiation broadcasts and and search responses, which is correct according to the UPnP/SSDP spec.</para>
+ /// </remarks>
+ public bool SupportPnpRootDevice
+ {
+ get { return _SupportPnpRootDevice; }
+ set
+ {
+ _SupportPnpRootDevice = value;
+ }
+ }
+
+ /// <summary>
+ /// Stops listening for requests, stops sending periodic broadcasts, disposes all internal resources.
+ /// </summary>
+ /// <param name="disposing"></param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ DisposeRebroadcastTimer();
+
+ var commsServer = _CommsServer;
+ if (commsServer != null)
+ {
+ commsServer.RequestReceived -= this.CommsServer_RequestReceived;
+ }
+
+ var tasks = Devices.ToList().Select(RemoveDevice).ToArray();
+ Task.WaitAll(tasks);
+
+ _CommsServer = null;
+ if (commsServer != null)
+ {
+ if (!commsServer.IsShared)
+ commsServer.Dispose();
+ }
+
+ _RecentSearchRequests = null;
+ }
+ }
+
+ private void ProcessSearchRequest(string mx, string searchTarget, IpEndPointInfo remoteEndPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
+ {
+ if (String.IsNullOrEmpty(searchTarget))
+ {
+ WriteTrace(String.Format("Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
+ return;
+ }
+
+ //WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget));
+
+ if (IsDuplicateSearchRequest(searchTarget, remoteEndPoint))
+ {
+ //WriteTrace("Search Request is Duplicate, ignoring.");
+ return;
+ }
+
+ //Wait on random interval up to MX, as per SSDP spec.
+ //Also, as per UPnP 1.1/SSDP spec ignore missing/bank MX header. If over 120, assume random value between 0 and 120.
+ //Using 16 as minimum as that's often the minimum system clock frequency anyway.
+ int maxWaitInterval = 0;
+ if (String.IsNullOrEmpty(mx))
+ {
+ //Windows Explorer is poorly behaved and doesn't supply an MX header value.
+ //if (this.SupportPnpRootDevice)
+ mx = "1";
+ //else
+ //return;
+ }
+
+ if (!Int32.TryParse(mx, out maxWaitInterval) || maxWaitInterval <= 0) return;
+
+ if (maxWaitInterval > 120)
+ maxWaitInterval = _Random.Next(0, 120);
+
+ //Do not block synchronously as that may tie up a threadpool thread for several seconds.
+ Task.Delay(_Random.Next(16, (maxWaitInterval * 1000))).ContinueWith((parentTask) =>
+ {
+ //Copying devices to local array here to avoid threading issues/enumerator exceptions.
+ IEnumerable<SsdpDevice> devices = null;
+ lock (_Devices)
+ {
+ if (String.Compare(SsdpConstants.SsdpDiscoverAllSTHeader, searchTarget, StringComparison.OrdinalIgnoreCase) == 0)
+ devices = GetAllDevicesAsFlatEnumerable().ToArray();
+ else if (String.Compare(SsdpConstants.UpnpDeviceTypeRootDevice, searchTarget, StringComparison.OrdinalIgnoreCase) == 0 || (this.SupportPnpRootDevice && String.Compare(SsdpConstants.PnpDeviceTypeRootDevice, searchTarget, StringComparison.OrdinalIgnoreCase) == 0))
+ devices = _Devices.ToArray();
+ else if (searchTarget.Trim().StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
+ devices = (from device in GetAllDevicesAsFlatEnumerable() where String.Compare(device.Uuid, searchTarget.Substring(5), StringComparison.OrdinalIgnoreCase) == 0 select device).ToArray();
+ else if (searchTarget.StartsWith("urn:", StringComparison.OrdinalIgnoreCase))
+ devices = (from device in GetAllDevicesAsFlatEnumerable() where String.Compare(device.FullDeviceType, searchTarget, StringComparison.OrdinalIgnoreCase) == 0 select device).ToArray();
+ }
+
+ if (devices != null)
+ {
+ var deviceList = devices.ToList();
+ //WriteTrace(String.Format("Sending {0} search responses", deviceList.Count));
+
+ foreach (var device in deviceList)
+ {
+ SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken);
+ }
+ }
+ else
+ {
+ //WriteTrace(String.Format("Sending 0 search responses."));
+ }
+ });
+ }
+
+ private IEnumerable<SsdpDevice> GetAllDevicesAsFlatEnumerable()
+ {
+ return _Devices.Union(_Devices.SelectManyRecursive<SsdpDevice>((d) => d.Devices));
+ }
+
+ private void SendDeviceSearchResponses(SsdpDevice device, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
+ {
+ bool isRootDevice = (device as SsdpRootDevice) != null;
+ if (isRootDevice)
+ {
+ SendSearchResponse(SsdpConstants.UpnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress, cancellationToken);
+ if (this.SupportPnpRootDevice)
+ SendSearchResponse(SsdpConstants.PnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress, cancellationToken);
+ }
+
+ SendSearchResponse(device.Udn, device, device.Udn, endPoint, receivedOnlocalIpAddress, cancellationToken);
+
+ SendSearchResponse(device.FullDeviceType, device, GetUsn(device.Udn, device.FullDeviceType), endPoint, receivedOnlocalIpAddress, cancellationToken);
+ }
+
+ private string GetUsn(string udn, string fullDeviceType)
+ {
+ return String.Format("{0}::{1}", udn, fullDeviceType);
+ }
+
+ private async void SendSearchResponse(string searchTarget, SsdpDevice device, string uniqueServiceName, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
+ {
+ var rootDevice = device.ToRootDevice();
+
+ //var additionalheaders = FormatCustomHeadersForResponse(device);
+
+ const string header = "HTTP/1.1 200 OK";
+
+ var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ values["EXT"] = "";
+ values["DATE"] = DateTime.UtcNow.ToString("r");
+ values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
+ values["ST"] = searchTarget;
+ values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+ values["USN"] = uniqueServiceName;
+ values["LOCATION"] = rootDevice.Location.ToString();
+
+ var message = BuildMessage(header, values);
+
+ try
+ {
+ await _CommsServer.SendMessage(System.Text.Encoding.UTF8.GetBytes(message), endPoint, receivedOnlocalIpAddress, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+
+ }
+
+ //WriteTrace(String.Format("Sent search response to " + endPoint.ToString()), device);
+ }
+
+ private bool IsDuplicateSearchRequest(string searchTarget, IpEndPointInfo endPoint)
+ {
+ var isDuplicateRequest = false;
+
+ var newRequest = new SearchRequest() { EndPoint = endPoint, SearchTarget = searchTarget, Received = DateTime.UtcNow };
+ lock (_RecentSearchRequests)
+ {
+ if (_RecentSearchRequests.ContainsKey(newRequest.Key))
+ {
+ var lastRequest = _RecentSearchRequests[newRequest.Key];
+ if (lastRequest.IsOld())
+ _RecentSearchRequests[newRequest.Key] = newRequest;
+ else
+ isDuplicateRequest = true;
+ }
+ else
+ {
+ _RecentSearchRequests.Add(newRequest.Key, newRequest);
+ if (_RecentSearchRequests.Count > 10)
+ CleanUpRecentSearchRequestsAsync();
+ }
+ }
+
+ return isDuplicateRequest;
+ }
+
+ private void CleanUpRecentSearchRequestsAsync()
+ {
+ lock (_RecentSearchRequests)
+ {
+ foreach (var requestKey in (from r in _RecentSearchRequests where r.Value.IsOld() select r.Key).ToArray())
+ {
+ _RecentSearchRequests.Remove(requestKey);
+ }
+ }
+ }
+
+ private void SendAllAliveNotifications(object state)
+ {
+ try
+ {
+ if (IsDisposed) return;
+
+ //WriteTrace("Begin Sending Alive Notifications For All Devices");
+
+ SsdpRootDevice[] devices;
+ lock (_Devices)
+ {
+ devices = _Devices.ToArray();
+ }
+
+ foreach (var device in devices)
+ {
+ if (IsDisposed) return;
+
+ SendAliveNotifications(device, true, CancellationToken.None);
+ }
+
+ //WriteTrace("Completed Sending Alive Notifications For All Devices");
+ }
+ catch (ObjectDisposedException ex)
+ {
+ WriteTrace("Publisher stopped, exception " + ex.Message);
+ Dispose();
+ }
+ }
+
+ private void SendAliveNotifications(SsdpDevice device, bool isRoot, CancellationToken cancellationToken)
+ {
+ if (isRoot)
+ {
+ SendAliveNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), cancellationToken);
+ if (this.SupportPnpRootDevice)
+ SendAliveNotification(device, SsdpConstants.PnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice), cancellationToken);
+ }
+
+ SendAliveNotification(device, device.Udn, device.Udn, cancellationToken);
+ SendAliveNotification(device, device.FullDeviceType, GetUsn(device.Udn, device.FullDeviceType), cancellationToken);
+
+ foreach (var childDevice in device.Devices)
+ {
+ SendAliveNotifications(childDevice, false, cancellationToken);
+ }
+ }
+
+ private void SendAliveNotification(SsdpDevice device, string notificationType, string uniqueServiceName, CancellationToken cancellationToken)
+ {
+ var rootDevice = device.ToRootDevice();
+
+ const string header = "NOTIFY * HTTP/1.1";
+
+ var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ // If needed later for non-server devices, these headers will need to be dynamic
+ values["HOST"] = "239.255.255.250:1900";
+ values["DATE"] = DateTime.UtcNow.ToString("r");
+ values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
+ values["LOCATION"] = rootDevice.Location.ToString();
+ values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+ values["NTS"] = "ssdp:alive";
+ values["NT"] = notificationType;
+ values["USN"] = uniqueServiceName;
+
+ var message = BuildMessage(header, values);
+
+ _CommsServer.SendMulticastMessage(message, cancellationToken);
+
+ //WriteTrace(String.Format("Sent alive notification"), device);
+ }
+
+ private Task SendByeByeNotifications(SsdpDevice device, bool isRoot, CancellationToken cancellationToken)
+ {
+ var tasks = new List<Task>();
+ if (isRoot)
+ {
+ tasks.Add(SendByeByeNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), cancellationToken));
+ if (this.SupportPnpRootDevice)
+ tasks.Add(SendByeByeNotification(device, "pnp:rootdevice", GetUsn(device.Udn, "pnp:rootdevice"), cancellationToken));
+ }
+
+ tasks.Add(SendByeByeNotification(device, device.Udn, device.Udn, cancellationToken));
+ tasks.Add(SendByeByeNotification(device, String.Format("urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken));
+
+ foreach (var childDevice in device.Devices)
+ {
+ tasks.Add(SendByeByeNotifications(childDevice, false, cancellationToken));
+ }
+
+ return Task.WhenAll(tasks);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "byebye", Justification = "Correct value for this type of notification in SSDP.")]
+ private Task SendByeByeNotification(SsdpDevice device, string notificationType, string uniqueServiceName, CancellationToken cancellationToken)
+ {
+ const string header = "NOTIFY * HTTP/1.1";
+
+ var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ // If needed later for non-server devices, these headers will need to be dynamic
+ values["HOST"] = "239.255.255.250:1900";
+ values["DATE"] = DateTime.UtcNow.ToString("r");
+ values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
+ values["NTS"] = "ssdp:byebye";
+ values["NT"] = notificationType;
+ values["USN"] = uniqueServiceName;
+
+ var message = BuildMessage(header, values);
+
+ var sendCount = IsDisposed ? 1 : 3;
+ WriteTrace(String.Format("Sent byebye notification"), device);
+ return _CommsServer.SendMulticastMessage(message, sendCount, cancellationToken);
+ }
+
+ private void DisposeRebroadcastTimer()
+ {
+ var timer = _RebroadcastAliveNotificationsTimer;
+ _RebroadcastAliveNotificationsTimer = null;
+ if (timer != null)
+ timer.Dispose();
+ }
+
+ private TimeSpan GetMinimumNonZeroCacheLifetime()
+ {
+ var nonzeroCacheLifetimesQuery = (from device
+ in _Devices
+ where device.CacheLifetime != TimeSpan.Zero
+ select device.CacheLifetime).ToList();
+
+ if (nonzeroCacheLifetimesQuery.Any())
+ return nonzeroCacheLifetimesQuery.Min();
+ else
+ return TimeSpan.Zero;
+ }
+
+ private string GetFirstHeaderValue(System.Net.Http.Headers.HttpRequestHeaders httpRequestHeaders, string headerName)
+ {
+ string retVal = null;
+ IEnumerable<String> values = null;
+ if (httpRequestHeaders.TryGetValues(headerName, out values) && values != null)
+ retVal = values.FirstOrDefault();
+
+ return retVal;
+ }
+
+ public Action<string> LogFunction { get; set; }
+
+ private void WriteTrace(string text)
+ {
+ if (LogFunction != null)
+ {
+ LogFunction(text);
+ }
+ //System.Diagnostics.Debug.WriteLine(text, "SSDP Publisher");
+ }
+
+ private void WriteTrace(string text, SsdpDevice device)
+ {
+ var rootDevice = device as SsdpRootDevice;
+ if (rootDevice != null)
+ WriteTrace(text + " " + device.DeviceType + " - " + device.Uuid + " - " + rootDevice.Location);
+ else
+ WriteTrace(text + " " + device.DeviceType + " - " + device.Uuid);
+ }
+
+ private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
+ {
+ if (this.IsDisposed) return;
+
+ if (string.Equals(e.Message.Method.Method, SsdpConstants.MSearchMethod, StringComparison.OrdinalIgnoreCase))
+ {
+ //According to SSDP/UPnP spec, ignore message if missing these headers.
+ // Edit: But some devices do it anyway
+ //if (!e.Message.Headers.Contains("MX"))
+ // WriteTrace("Ignoring search request - missing MX header.");
+ //else if (!e.Message.Headers.Contains("MAN"))
+ // WriteTrace("Ignoring search request - missing MAN header.");
+ //else
+ ProcessSearchRequest(GetFirstHeaderValue(e.Message.Headers, "MX"), GetFirstHeaderValue(e.Message.Headers, "ST"), e.ReceivedFrom, e.LocalIpAddress, CancellationToken.None);
+ }
+ }
+
+ private class SearchRequest
+ {
+ public IpEndPointInfo EndPoint { get; set; }
+ public DateTime Received { get; set; }
+ public string SearchTarget { get; set; }
+
+ public string Key
+ {
+ get { return this.SearchTarget + ":" + this.EndPoint.ToString(); }
+ }
+
+ public bool IsOld()
+ {
+ return DateTime.UtcNow.Subtract(this.Received).TotalMilliseconds > 500;
+ }
+ }
}
} \ No newline at end of file
diff --git a/RSSDP/SsdpDevicePublisherBase.cs b/RSSDP/SsdpDevicePublisherBase.cs
deleted file mode 100644
index eda769da6..000000000
--- a/RSSDP/SsdpDevicePublisherBase.cs
+++ /dev/null
@@ -1,709 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Net.Http;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Threading;
-using RSSDP;
-
-namespace Rssdp.Infrastructure
-{
- /// <summary>
- /// Provides the platform independent logic for publishing SSDP devices (notifications and search responses).
- /// </summary>
- public abstract class SsdpDevicePublisherBase : DisposableManagedObjectBase, ISsdpDevicePublisher
- {
-
- #region Fields & Constants
-
- private ISsdpCommunicationsServer _CommsServer;
- private string _OSName;
- private string _OSVersion;
-
- private bool _SupportPnpRootDevice;
-
- private IList<SsdpRootDevice> _Devices;
- private IReadOnlyList<SsdpRootDevice> _ReadOnlyDevices;
-
- private ITimer _RebroadcastAliveNotificationsTimer;
- private ITimerFactory _timerFactory;
- //private TimeSpan _RebroadcastAliveNotificationsTimeSpan;
- private DateTime _LastNotificationTime;
-
- private IDictionary<string, SearchRequest> _RecentSearchRequests;
- private IUpnpDeviceValidator _DeviceValidator;
-
- private Random _Random;
- //private TimeSpan _MinCacheTime;
-
- private const string ServerVersion = "1.0";
-
- #endregion
-
- #region Message Format Constants
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- protected SsdpDevicePublisherBase(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory, string osName, string osVersion)
- {
- if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
- if (osName == null) throw new ArgumentNullException("osName");
- if (osName.Length == 0) throw new ArgumentException("osName cannot be an empty string.", "osName");
- if (osVersion == null) throw new ArgumentNullException("osVersion");
- if (osVersion.Length == 0) throw new ArgumentException("osVersion cannot be an empty string.", "osName");
-
- _SupportPnpRootDevice = true;
- _timerFactory = timerFactory;
- _Devices = new List<SsdpRootDevice>();
- _ReadOnlyDevices = new ReadOnlyCollection<SsdpRootDevice>(_Devices);
- _RecentSearchRequests = new Dictionary<string, SearchRequest>(StringComparer.OrdinalIgnoreCase);
- _Random = new Random();
- _DeviceValidator = new Upnp10DeviceValidator(); //Should probably inject this later, but for now we only support 1.0.
-
- _CommsServer = communicationsServer;
- _CommsServer.RequestReceived += CommsServer_RequestReceived;
- _OSName = osName;
- _OSVersion = osVersion;
-
- _CommsServer.BeginListeningForBroadcasts();
- }
-
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients.
- /// </summary>
- /// <remarks>
- /// <para>Adding a device causes "alive" notification messages to be sent immediately, or very soon after. Ensure your device/description service is running before adding the device object here.</para>
- /// <para>Devices added here with a non-zero cache life time will also have notifications broadcast periodically.</para>
- /// <para>This method ignores duplicate device adds (if the same device instance is added multiple times, the second and subsequent add calls do nothing).</para>
- /// </remarks>
- /// <param name="device">The <see cref="SsdpDevice"/> instance to add.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
- /// <exception cref="System.InvalidOperationException">Thrown if the <paramref name="device"/> contains property values that are not acceptable to the UPnP 1.0 specification.</exception>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable supresses compiler warning, but task is not really needed.")]
- public void AddDevice(SsdpRootDevice device)
- {
- if (device == null) throw new ArgumentNullException("device");
-
- ThrowIfDisposed();
-
- _DeviceValidator.ThrowIfDeviceInvalid(device);
-
- TimeSpan minCacheTime = TimeSpan.Zero;
- bool wasAdded = false;
- lock (_Devices)
- {
- if (!_Devices.Contains(device))
- {
- _Devices.Add(device);
- wasAdded = true;
- minCacheTime = GetMinimumNonZeroCacheLifetime();
- }
- }
-
- if (wasAdded)
- {
- //_MinCacheTime = minCacheTime;
-
- ConnectToDeviceEvents(device);
-
- WriteTrace("Device Added", device);
-
- SetRebroadcastAliveNotificationsTimer(minCacheTime);
-
- SendAliveNotifications(device, true, CancellationToken.None);
- }
- }
-
- /// <summary>
- /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable.
- /// </summary>
- /// <remarks>
- /// <para>Removing a device causes "byebye" notification messages to be sent immediately, advising clients of the device/service becoming unavailable. We recommend removing the device from the published list before shutting down the actual device/service, if possible.</para>
- /// <para>This method does nothing if the device was not found in the collection.</para>
- /// </remarks>
- /// <param name="device">The <see cref="SsdpDevice"/> instance to add.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
- public async Task RemoveDevice(SsdpRootDevice device)
- {
- if (device == null) throw new ArgumentNullException("device");
-
- ThrowIfDisposed();
-
- bool wasRemoved = false;
- TimeSpan minCacheTime = TimeSpan.Zero;
- lock (_Devices)
- {
- if (_Devices.Contains(device))
- {
- _Devices.Remove(device);
- wasRemoved = true;
- minCacheTime = GetMinimumNonZeroCacheLifetime();
- }
- }
-
- if (wasRemoved)
- {
- //_MinCacheTime = minCacheTime;
-
- DisconnectFromDeviceEvents(device);
-
- WriteTrace("Device Removed", device);
-
- await SendByeByeNotifications(device, true, CancellationToken.None).ConfigureAwait(false);
-
- SetRebroadcastAliveNotificationsTimer(minCacheTime);
- }
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Returns a read only list of devices being published by this instance.
- /// </summary>
- public IEnumerable<SsdpRootDevice> Devices
- {
- get
- {
- return _ReadOnlyDevices;
- }
- }
-
- /// <summary>
- /// If true (default) treats root devices as both upnp:rootdevice and pnp:rootdevice types.
- /// </summary>
- /// <remarks>
- /// <para>Enabling this option will cause devices to show up in Microsoft Windows Explorer's network screens (if discovery is enabled etc.). Windows Explorer appears to search only for pnp:rootdeivce and not upnp:rootdevice.</para>
- /// <para>If false, the system will only use upnp:rootdevice for notifiation broadcasts and and search responses, which is correct according to the UPnP/SSDP spec.</para>
- /// </remarks>
- public bool SupportPnpRootDevice
- {
- get { return _SupportPnpRootDevice; }
- set
- {
- _SupportPnpRootDevice = value;
- }
- }
-
- #endregion
-
- #region Overrides
-
- /// <summary>
- /// Stops listening for requests, stops sending periodic broadcasts, disposes all internal resources.
- /// </summary>
- /// <param name="disposing"></param>
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- var commsServer = _CommsServer;
- _CommsServer = null;
-
- if (commsServer != null)
- {
- commsServer.RequestReceived -= this.CommsServer_RequestReceived;
- if (!commsServer.IsShared)
- commsServer.Dispose();
- }
-
- DisposeRebroadcastTimer();
-
- foreach (var device in this.Devices)
- {
- DisconnectFromDeviceEvents(device);
- }
-
- _RecentSearchRequests = null;
- }
- }
-
- #endregion
-
- #region Private Methods
-
- #region Search Related Methods
-
- private void ProcessSearchRequest(string mx, string searchTarget, IpEndPointInfo remoteEndPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
- {
- if (String.IsNullOrEmpty(searchTarget))
- {
- WriteTrace(String.Format("Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
- return;
- }
-
- //WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget));
-
- if (IsDuplicateSearchRequest(searchTarget, remoteEndPoint))
- {
- //WriteTrace("Search Request is Duplicate, ignoring.");
- return;
- }
-
- //Wait on random interval up to MX, as per SSDP spec.
- //Also, as per UPnP 1.1/SSDP spec ignore missing/bank MX header. If over 120, assume random value between 0 and 120.
- //Using 16 as minimum as that's often the minimum system clock frequency anyway.
- int maxWaitInterval = 0;
- if (String.IsNullOrEmpty(mx))
- {
- //Windows Explorer is poorly behaved and doesn't supply an MX header value.
- //if (this.SupportPnpRootDevice)
- mx = "1";
- //else
- //return;
- }
-
- if (!Int32.TryParse(mx, out maxWaitInterval) || maxWaitInterval <= 0) return;
-
- if (maxWaitInterval > 120)
- maxWaitInterval = _Random.Next(0, 120);
-
- //Do not block synchronously as that may tie up a threadpool thread for several seconds.
- Task.Delay(_Random.Next(16, (maxWaitInterval * 1000))).ContinueWith((parentTask) =>
- {
- //Copying devices to local array here to avoid threading issues/enumerator exceptions.
- IEnumerable<SsdpDevice> devices = null;
- lock (_Devices)
- {
- if (String.Compare(SsdpConstants.SsdpDiscoverAllSTHeader, searchTarget, StringComparison.OrdinalIgnoreCase) == 0)
- devices = GetAllDevicesAsFlatEnumerable().ToArray();
- else if (String.Compare(SsdpConstants.UpnpDeviceTypeRootDevice, searchTarget, StringComparison.OrdinalIgnoreCase) == 0 || (this.SupportPnpRootDevice && String.Compare(SsdpConstants.PnpDeviceTypeRootDevice, searchTarget, StringComparison.OrdinalIgnoreCase) == 0))
- devices = _Devices.ToArray();
- else if (searchTarget.Trim().StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
- devices = (from device in GetAllDevicesAsFlatEnumerable() where String.Compare(device.Uuid, searchTarget.Substring(5), StringComparison.OrdinalIgnoreCase) == 0 select device).ToArray();
- else if (searchTarget.StartsWith("urn:", StringComparison.OrdinalIgnoreCase))
- devices = (from device in GetAllDevicesAsFlatEnumerable() where String.Compare(device.FullDeviceType, searchTarget, StringComparison.OrdinalIgnoreCase) == 0 select device).ToArray();
- }
-
- if (devices != null)
- {
- var deviceList = devices.ToList();
- //WriteTrace(String.Format("Sending {0} search responses", deviceList.Count));
-
- foreach (var device in deviceList)
- {
- SendDeviceSearchResponses(device, remoteEndPoint, receivedOnlocalIpAddress, cancellationToken);
- }
- }
- else
- {
- //WriteTrace(String.Format("Sending 0 search responses."));
- }
- });
- }
-
- private IEnumerable<SsdpDevice> GetAllDevicesAsFlatEnumerable()
- {
- return _Devices.Union(_Devices.SelectManyRecursive<SsdpDevice>((d) => d.Devices));
- }
-
- private void SendDeviceSearchResponses(SsdpDevice device, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
- {
- bool isRootDevice = (device as SsdpRootDevice) != null;
- if (isRootDevice)
- {
- SendSearchResponse(SsdpConstants.UpnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress, cancellationToken);
- if (this.SupportPnpRootDevice)
- SendSearchResponse(SsdpConstants.PnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress, cancellationToken);
- }
-
- SendSearchResponse(device.Udn, device, device.Udn, endPoint, receivedOnlocalIpAddress, cancellationToken);
-
- SendSearchResponse(device.FullDeviceType, device, GetUsn(device.Udn, device.FullDeviceType), endPoint, receivedOnlocalIpAddress, cancellationToken);
- }
-
- private static string GetUsn(string udn, string fullDeviceType)
- {
- return String.Format("{0}::{1}", udn, fullDeviceType);
- }
-
- private async void SendSearchResponse(string searchTarget, SsdpDevice device, string uniqueServiceName, IpEndPointInfo endPoint, IpAddressInfo receivedOnlocalIpAddress, CancellationToken cancellationToken)
- {
- var rootDevice = device.ToRootDevice();
-
- //var additionalheaders = FormatCustomHeadersForResponse(device);
-
- const string header = "HTTP/1.1 200 OK";
-
- var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- values["EXT"] = "";
- values["DATE"] = DateTime.UtcNow.ToString("r");
- values["CACHE-CONTROL"] = "max-age = 600";
- values["ST"] = searchTarget;
- values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
- values["USN"] = uniqueServiceName;
- values["LOCATION"] = rootDevice.Location.ToString();
-
- var message = SsdpHelper.BuildMessage(header, values);
-
- try
- {
- await _CommsServer.SendMessage(System.Text.Encoding.UTF8.GetBytes(message), endPoint, receivedOnlocalIpAddress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
-
- }
-
- //WriteTrace(String.Format("Sent search response to " + endPoint.ToString()), device);
- }
-
- private bool IsDuplicateSearchRequest(string searchTarget, IpEndPointInfo endPoint)
- {
- var isDuplicateRequest = false;
-
- var newRequest = new SearchRequest() { EndPoint = endPoint, SearchTarget = searchTarget, Received = DateTime.UtcNow };
- lock (_RecentSearchRequests)
- {
- if (_RecentSearchRequests.ContainsKey(newRequest.Key))
- {
- var lastRequest = _RecentSearchRequests[newRequest.Key];
- if (lastRequest.IsOld())
- _RecentSearchRequests[newRequest.Key] = newRequest;
- else
- isDuplicateRequest = true;
- }
- else
- {
- _RecentSearchRequests.Add(newRequest.Key, newRequest);
- if (_RecentSearchRequests.Count > 10)
- CleanUpRecentSearchRequestsAsync();
- }
- }
-
- return isDuplicateRequest;
- }
-
- private void CleanUpRecentSearchRequestsAsync()
- {
- lock (_RecentSearchRequests)
- {
- foreach (var requestKey in (from r in _RecentSearchRequests where r.Value.IsOld() select r.Key).ToArray())
- {
- _RecentSearchRequests.Remove(requestKey);
- }
- }
- }
-
- #endregion
-
- #region Notification Related Methods
-
- #region Alive
-
- private void SendAllAliveNotifications(object state)
- {
- try
- {
- if (IsDisposed) return;
-
- //DisposeRebroadcastTimer();
-
- //WriteTrace("Begin Sending Alive Notifications For All Devices");
-
- _LastNotificationTime = DateTime.Now;
-
- IEnumerable<SsdpRootDevice> devices;
- lock (_Devices)
- {
- devices = _Devices.ToArray();
- }
-
- foreach (var device in devices)
- {
- if (IsDisposed) return;
-
- SendAliveNotifications(device, true, CancellationToken.None);
- }
-
- //WriteTrace("Completed Sending Alive Notifications For All Devices");
- }
- catch (ObjectDisposedException ex)
- {
- WriteTrace("Publisher stopped, exception " + ex.Message);
- Dispose();
- }
- //finally
- //{
- // // This is causing all notifications to stop
- // //if (!this.IsDisposed)
- // //SetRebroadcastAliveNotificationsTimer(_MinCacheTime);
- //}
- }
-
- private void SendAliveNotifications(SsdpDevice device, bool isRoot, CancellationToken cancellationToken)
- {
- if (isRoot)
- {
- SendAliveNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), cancellationToken);
- if (this.SupportPnpRootDevice)
- SendAliveNotification(device, SsdpConstants.PnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice), cancellationToken);
- }
-
- SendAliveNotification(device, device.Udn, device.Udn, cancellationToken);
- SendAliveNotification(device, device.FullDeviceType, GetUsn(device.Udn, device.FullDeviceType), cancellationToken);
-
- foreach (var childDevice in device.Devices)
- {
- SendAliveNotifications(childDevice, false, cancellationToken);
- }
- }
-
- private void SendAliveNotification(SsdpDevice device, string notificationType, string uniqueServiceName, CancellationToken cancellationToken)
- {
- var rootDevice = device.ToRootDevice();
-
- const string header = "NOTIFY * HTTP/1.1";
-
- var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- // If needed later for non-server devices, these headers will need to be dynamic
- values["HOST"] = "239.255.255.250:1900";
- values["DATE"] = DateTime.UtcNow.ToString("r");
- values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
- values["LOCATION"] = rootDevice.Location.ToString();
- values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
- values["NTS"] = "ssdp:alive";
- values["NT"] = notificationType;
- values["USN"] = uniqueServiceName;
-
- var message = SsdpHelper.BuildMessage(header, values);
-
- _CommsServer.SendMulticastMessage(message, cancellationToken);
-
- //WriteTrace(String.Format("Sent alive notification"), device);
- }
-
- #endregion
-
- #region ByeBye
-
- private async Task SendByeByeNotifications(SsdpDevice device, bool isRoot, CancellationToken cancellationToken)
- {
- if (isRoot)
- {
- await SendByeByeNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), cancellationToken).ConfigureAwait(false);
- if (this.SupportPnpRootDevice)
- await SendByeByeNotification(device, "pnp:rootdevice", GetUsn(device.Udn, "pnp:rootdevice"), cancellationToken).ConfigureAwait(false); ;
- }
-
- await SendByeByeNotification(device, device.Udn, device.Udn, cancellationToken).ConfigureAwait(false); ;
- await SendByeByeNotification(device, String.Format("urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken).ConfigureAwait(false); ;
-
- foreach (var childDevice in device.Devices)
- {
- await SendByeByeNotifications(childDevice, false, cancellationToken).ConfigureAwait(false); ;
- }
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "byebye", Justification = "Correct value for this type of notification in SSDP.")]
- private Task SendByeByeNotification(SsdpDevice device, string notificationType, string uniqueServiceName, CancellationToken cancellationToken)
- {
- const string header = "NOTIFY * HTTP/1.1";
-
- var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- // If needed later for non-server devices, these headers will need to be dynamic
- values["HOST"] = "239.255.255.250:1900";
- values["DATE"] = DateTime.UtcNow.ToString("r");
- values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
- values["NTS"] = "ssdp:byebye";
- values["NT"] = notificationType;
- values["USN"] = uniqueServiceName;
-
- var message = SsdpHelper.BuildMessage(header, values);
-
- return _CommsServer.SendMulticastMessage(message, cancellationToken);
-
- //WriteTrace(String.Format("Sent byebye notification"), device);
- }
-
- #endregion
-
- #region Rebroadcast Timer
-
- private void DisposeRebroadcastTimer()
- {
- var timer = _RebroadcastAliveNotificationsTimer;
- _RebroadcastAliveNotificationsTimer = null;
- if (timer != null)
- timer.Dispose();
- }
-
- private void SetRebroadcastAliveNotificationsTimer(TimeSpan minCacheTime)
- {
- //if (minCacheTime == _RebroadcastAliveNotificationsTimeSpan) return;
-
- DisposeRebroadcastTimer();
-
- if (minCacheTime == TimeSpan.Zero) return;
-
- // According to UPnP/SSDP spec, we should randomise the interval at
- // which we broadcast notifications, to help with network congestion.
- // Specs also advise to choose a random interval up to *half* the cache time.
- // Here we do that, but using the minimum non-zero cache time of any device we are publishing.
- var rebroadCastInterval = new TimeSpan(minCacheTime.Ticks);
-
- // If we were already setup to rebroadcast someime in the future,
- // don't just blindly reset the next broadcast time to the new interval
- // as repeatedly changing the interval might end up causing us to over
- // delay in sending the next one.
- var nextBroadcastInterval = rebroadCastInterval;
- if (_LastNotificationTime != DateTime.MinValue)
- {
- nextBroadcastInterval = rebroadCastInterval.Subtract(DateTime.Now.Subtract(_LastNotificationTime));
- if (nextBroadcastInterval.Ticks < 0)
- nextBroadcastInterval = TimeSpan.Zero;
- else if (nextBroadcastInterval > rebroadCastInterval)
- nextBroadcastInterval = rebroadCastInterval;
- }
-
- //_RebroadcastAliveNotificationsTimeSpan = rebroadCastInterval;
- _RebroadcastAliveNotificationsTimer = _timerFactory.Create(SendAllAliveNotifications, null, nextBroadcastInterval, rebroadCastInterval);
-
- WriteTrace(String.Format("Rebroadcast Interval = {0}, Next Broadcast At = {1}", rebroadCastInterval.ToString(), nextBroadcastInterval.ToString()));
- }
-
- private TimeSpan GetMinimumNonZeroCacheLifetime()
- {
- var nonzeroCacheLifetimesQuery = (from device
- in _Devices
- where device.CacheLifetime != TimeSpan.Zero
- select device.CacheLifetime).ToList();
-
- if (nonzeroCacheLifetimesQuery.Any())
- return nonzeroCacheLifetimesQuery.Min();
- else
- return TimeSpan.Zero;
- }
-
- #endregion
-
- #endregion
-
- private static string GetFirstHeaderValue(System.Net.Http.Headers.HttpRequestHeaders httpRequestHeaders, string headerName)
- {
- string retVal = null;
- IEnumerable<String> values = null;
- if (httpRequestHeaders.TryGetValues(headerName, out values) && values != null)
- retVal = values.FirstOrDefault();
-
- return retVal;
- }
-
- public static Action<string> LogFunction { get; set; }
-
- private static void WriteTrace(string text)
- {
- if (LogFunction != null)
- {
- LogFunction(text);
- }
- //System.Diagnostics.Debug.WriteLine(text, "SSDP Publisher");
- }
-
- private static void WriteTrace(string text, SsdpDevice device)
- {
- var rootDevice = device as SsdpRootDevice;
- if (rootDevice != null)
- WriteTrace(text + " " + device.DeviceType + " - " + device.Uuid + " - " + rootDevice.Location);
- else
- WriteTrace(text + " " + device.DeviceType + " - " + device.Uuid);
- }
-
- private void ConnectToDeviceEvents(SsdpDevice device)
- {
- device.DeviceAdded += device_DeviceAdded;
- device.DeviceRemoved += device_DeviceRemoved;
-
- foreach (var childDevice in device.Devices)
- {
- ConnectToDeviceEvents(childDevice);
- }
- }
-
- private void DisconnectFromDeviceEvents(SsdpDevice device)
- {
- device.DeviceAdded -= device_DeviceAdded;
- device.DeviceRemoved -= device_DeviceRemoved;
-
- foreach (var childDevice in device.Devices)
- {
- DisconnectFromDeviceEvents(childDevice);
- }
- }
-
- #endregion
-
- #region Event Handlers
-
- private void device_DeviceAdded(object sender, DeviceEventArgs e)
- {
- SendAliveNotifications(e.Device, false, CancellationToken.None);
- ConnectToDeviceEvents(e.Device);
- }
-
- private void device_DeviceRemoved(object sender, DeviceEventArgs e)
- {
- var task = SendByeByeNotifications(e.Device, false, CancellationToken.None);
- Task.WaitAll(task);
- DisconnectFromDeviceEvents(e.Device);
- }
-
- private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
- {
- if (this.IsDisposed) return;
-
- if (string.Equals(e.Message.Method.Method, SsdpConstants.MSearchMethod, StringComparison.OrdinalIgnoreCase))
- {
- //According to SSDP/UPnP spec, ignore message if missing these headers.
- // Edit: But some devices do it anyway
- //if (!e.Message.Headers.Contains("MX"))
- // WriteTrace("Ignoring search request - missing MX header.");
- //else if (!e.Message.Headers.Contains("MAN"))
- // WriteTrace("Ignoring search request - missing MAN header.");
- //else
- ProcessSearchRequest(GetFirstHeaderValue(e.Message.Headers, "MX"), GetFirstHeaderValue(e.Message.Headers, "ST"), e.ReceivedFrom, e.LocalIpAddress, CancellationToken.None);
- }
- }
-
- #endregion
-
- #region Private Classes
-
- private class SearchRequest
- {
- public IpEndPointInfo EndPoint { get; set; }
- public DateTime Received { get; set; }
- public string SearchTarget { get; set; }
-
- public string Key
- {
- get { return this.SearchTarget + ":" + this.EndPoint.ToString(); }
- }
-
- public bool IsOld()
- {
- return DateTime.UtcNow.Subtract(this.Received).TotalMilliseconds > 500;
- }
- }
-
- #endregion
-
- }
-} \ No newline at end of file
diff --git a/RSSDP/SsdpEmbeddedDevice.cs b/RSSDP/SsdpEmbeddedDevice.cs
index 28948f950..dca1ff5e3 100644
--- a/RSSDP/SsdpEmbeddedDevice.cs
+++ b/RSSDP/SsdpEmbeddedDevice.cs
@@ -25,17 +25,6 @@ namespace Rssdp
{
}
- /// <summary>
- /// Deserialisation constructor.
- /// </summary>
- /// <param name="deviceDescriptionXml">A UPnP device description XML document.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="deviceDescriptionXml"/> argument is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the <paramref name="deviceDescriptionXml"/> argument is empty.</exception>
- public SsdpEmbeddedDevice(string deviceDescriptionXml)
- : base(deviceDescriptionXml)
- {
- }
-
#endregion
#region Public Properties
diff --git a/RSSDP/SsdpHelper.cs b/RSSDP/SsdpHelper.cs
deleted file mode 100644
index 2eacf3c11..000000000
--- a/RSSDP/SsdpHelper.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Text;
-
-namespace RSSDP
-{
- public class SsdpHelper
- {
- private readonly ITextEncoding _encoding;
-
- public SsdpHelper(ITextEncoding encoding)
- {
- _encoding = encoding;
- }
-
- public SsdpMessageInfo ParseSsdpResponse(byte[] data)
- {
- using (var ms = new MemoryStream(data))
- {
- using (var reader = new StreamReader(ms, _encoding.GetASCIIEncoding()))
- {
- var proto = (reader.ReadLine() ?? string.Empty).Trim();
- var method = proto.Split(new[] { ' ' }, 2)[0];
- var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
- {
- line = line.Trim();
- if (string.IsNullOrEmpty(line))
- {
- break;
- }
- var parts = line.Split(new[] { ':' }, 2);
-
- if (parts.Length >= 2)
- {
- headers[parts[0]] = parts[1].Trim();
- }
- }
-
- return new SsdpMessageInfo
- {
- Method = method,
- Headers = headers,
- Message = data
- };
- }
- }
- }
-
- public static string BuildMessage(string header, Dictionary<string, string> values)
- {
- var builder = new StringBuilder();
-
- const string argFormat = "{0}: {1}\r\n";
-
- builder.AppendFormat("{0}\r\n", header);
-
- foreach (var pair in values)
- {
- builder.AppendFormat(argFormat, pair.Key, pair.Value);
- }
-
- builder.Append("\r\n");
-
- return builder.ToString();
- }
- }
-
- public class SsdpMessageInfo
- {
- public string Method { get; set; }
-
- public IpEndPointInfo EndPoint { get; set; }
-
- public Dictionary<string, string> Headers { get; set; }
-
- public IpEndPointInfo LocalEndPoint { get; set; }
- public byte[] Message { get; set; }
-
- public SsdpMessageInfo()
- {
- Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs
index 5b7d0f454..6d0fcafbb 100644
--- a/RSSDP/SsdpRootDevice.cs
+++ b/RSSDP/SsdpRootDevice.cs
@@ -31,25 +31,6 @@ namespace Rssdp
{
}
- /// <summary>
- /// Deserialisation constructor.
- /// </summary>
- /// <param name="location">The url from which the device description document was retrieved.</param>
- /// <param name="cacheLifetime">A <see cref="System.TimeSpan"/> representing the time maximum period of time the device description can be cached for.</param>
- /// <param name="deviceDescriptionXml">The device description XML as a string.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="deviceDescriptionXml"/> or <paramref name="location"/> arguments are null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the <paramref name="deviceDescriptionXml"/> argument is empty.</exception>
- public SsdpRootDevice(Uri location, TimeSpan cacheLifetime, string deviceDescriptionXml)
- : base(deviceDescriptionXml)
- {
- if (location == null) throw new ArgumentNullException("location");
-
- this.CacheLifetime = cacheLifetime;
- this.Location = location;
-
- LoadFromDescriptionDocument(deviceDescriptionXml);
- }
-
#endregion
#region Public Properties
@@ -94,82 +75,5 @@ namespace Rssdp
#endregion
- #region Public Methods
-
- /// <summary>
- /// Saves the property values of this device object to an a string in the full UPnP device description XML format, including child devices and outer root node and XML document declaration.
- /// </summary>
- /// <returns>A string containing XML in the UPnP device description format</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispsoing memory stream twice is 'safe' and easier to read than correct code for ensuring it is only closed once.")]
- public virtual string ToDescriptionDocument()
- {
- if (String.IsNullOrEmpty(this.Uuid)) throw new InvalidOperationException("Must provide a UUID value.");
-
- //This would have been so much nicer with Xml.Linq, but that's
- //not available until .NET 4.03 at the earliest, and I want to
- //target 4.0 :(
- using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
- {
- System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(ms, new XmlWriterSettings() { Encoding = System.Text.UTF8Encoding.UTF8, Indent = true, NamespaceHandling = NamespaceHandling.OmitDuplicates });
- writer.WriteStartDocument();
- writer.WriteStartElement("root", SsdpConstants.SsdpDeviceDescriptionXmlNamespace);
-
- writer.WriteStartElement("specVersion");
- writer.WriteElementString("major", "1");
- writer.WriteElementString("minor", "0");
- writer.WriteEndElement();
-
- if (this.UrlBase != null && this.UrlBase != this.Location)
- writer.WriteElementString("URLBase", this.UrlBase.ToString());
-
- WriteDeviceDescriptionXml(writer, this);
-
- writer.WriteEndElement();
- writer.Flush();
-
- ms.Seek(0, System.IO.SeekOrigin.Begin);
- using (var reader = new System.IO.StreamReader(ms))
- {
- return reader.ReadToEnd();
- }
- }
- }
-
- #endregion
-
- #region Private Methods
-
- #region Deserialisation Methods
-
- private void LoadFromDescriptionDocument(string deviceDescriptionXml)
- {
- using (var ms = new System.IO.MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(deviceDescriptionXml)))
- {
- var reader = XmlReader.Create(ms);
- while (!reader.EOF)
- {
- reader.Read();
- if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "root") continue;
-
- while (!reader.EOF)
- {
- reader.Read();
-
- if (reader.NodeType != XmlNodeType.Element) continue;
-
- if (reader.LocalName == "URLBase")
- {
- this.UrlBase = StringToUri(reader.ReadElementContentAsString());
- break;
- }
- }
- }
- }
- }
-
- #endregion
-
- #endregion
-
}
} \ No newline at end of file
diff --git a/RSSDP/UPnP10DeviceValidator.cs b/RSSDP/UPnP10DeviceValidator.cs
deleted file mode 100644
index 2a8a9ccd2..000000000
--- a/RSSDP/UPnP10DeviceValidator.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Rssdp.Infrastructure
-{
- /// <summary>
- /// Validates a <see cref="SsdpDevice"/> object's properties meet the UPnP 1.0 specification.
- /// </summary>
- /// <remarks>
- /// <para>This is a best effort validation for known rules, it doesn't guarantee 100% compatibility with the specification. Reading the specification yourself is the best way to ensure compatibility.</para>
- /// </remarks>
- public class Upnp10DeviceValidator : IUpnpDeviceValidator
- {
-
- #region Public Methods
-
- /// <summary>
- /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified root device.
- /// </summary>
- /// <remarks>
- /// <para>If no errors are found, an empty (but non-null) enumerable is returned.</para>
- /// </remarks>
- /// <param name="device">The <see cref="SsdpRootDevice"/> to validate.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
- /// <returns>A non-null enumerable set of strings, empty if there are no validation errors, otherwise each string represents a discrete problem.</returns>
- public List<string> GetValidationErrors(SsdpRootDevice device)
- {
- if (device == null) throw new ArgumentNullException("device");
-
- var retVal = GetValidationErrors((SsdpDevice)device);
-
- if (device.Location == null)
- retVal.Add("Location cannot be null.");
- else if (!device.Location.IsAbsoluteUri)
- retVal.Add("Location must be an absolute URL.");
-
- return retVal;
- }
-
- /// <summary>
- /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified device.
- /// </summary>
- /// <remarks>
- /// <para>If no errors are found, an empty (but non-null) enumerable is returned.</para>
- /// </remarks>
- /// <param name="device">The <see cref="SsdpDevice"/> to validate.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
- /// <returns>A non-null enumerable set of strings, empty if there are no validation errors, otherwise each string represents a discrete problem.</returns>
- public List<string> GetValidationErrors(SsdpDevice device)
- {
- if (device == null) throw new ArgumentNullException("device");
-
- var retVal = new List<string>();
-
- if (String.IsNullOrEmpty(device.Uuid))
- retVal.Add("Uuid is not set.");
-
- if (!String.IsNullOrEmpty(device.Upc))
- ValidateUpc(device, retVal);
-
- if (String.IsNullOrEmpty(device.Udn))
- retVal.Add("UDN is not set.");
- else
- ValidateUdn(device, retVal);
-
- if (String.IsNullOrEmpty(device.DeviceType))
- retVal.Add("DeviceType is not set.");
-
- if (String.IsNullOrEmpty(device.DeviceTypeNamespace))
- retVal.Add("DeviceTypeNamespace is not set.");
- else
- {
- if (IsOverLength(device.DeviceTypeNamespace, 64))
- retVal.Add("DeviceTypeNamespace cannot be longer than 64 characters.");
-
- //if (device.DeviceTypeNamespace.Contains("."))
- // retVal.Add("Period (.) characters in the DeviceTypeNamespace property must be replaced with hyphens (-).");
- }
-
- if (device.DeviceVersion <= 0)
- retVal.Add("DeviceVersion must be 1 or greater.");
-
- if (IsOverLength(device.ModelName, 32))
- retVal.Add("ModelName cannot be longer than 32 characters.");
-
- if (IsOverLength(device.ModelNumber, 32))
- retVal.Add("ModelNumber cannot be longer than 32 characters.");
-
- if (IsOverLength(device.FriendlyName, 64))
- retVal.Add("FriendlyName cannot be longer than 64 characters.");
-
- if (IsOverLength(device.Manufacturer, 64))
- retVal.Add("Manufacturer cannot be longer than 64 characters.");
-
- if (IsOverLength(device.SerialNumber, 64))
- retVal.Add("SerialNumber cannot be longer than 64 characters.");
-
- if (IsOverLength(device.ModelDescription, 128))
- retVal.Add("ModelDescription cannot be longer than 128 characters.");
-
- if (String.IsNullOrEmpty(device.FriendlyName))
- retVal.Add("FriendlyName is required.");
-
- if (String.IsNullOrEmpty(device.Manufacturer))
- retVal.Add("Manufacturer is required.");
-
- if (String.IsNullOrEmpty(device.ModelName))
- retVal.Add("ModelName is required.");
-
- if (device.Icons.Count > 0)
- ValidateIcons(device, retVal);
-
- ValidateChildDevices(device, retVal);
-
- return retVal;
- }
-
- /// <summary>
- /// Validates the specified device and throws an <see cref="System.InvalidOperationException"/> if there are any validation errors.
- /// </summary>
- /// <param name="device">The <see cref="SsdpDevice"/> to validate.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
- /// <exception cref="System.InvalidOperationException">Thrown if the device object does not pass validation.</exception>
- public void ThrowIfDeviceInvalid(SsdpDevice device)
- {
- var errors = this.GetValidationErrors(device);
- if (errors != null && errors.Count > 0) throw new InvalidOperationException("Invalid device settings : " + String.Join(Environment.NewLine, errors));
- }
-
- #endregion
-
- #region Private Methods
-
- private static void ValidateUpc(SsdpDevice device, List<string> retVal)
- {
- if (device.Upc.Length != 12)
- retVal.Add("Upc, if provided, should be 12 digits.");
-
- foreach (char c in device.Upc)
- {
- if (!Char.IsDigit(c))
- {
- retVal.Add("Upc, if provided, should contain only digits (numeric characters).");
- break;
- }
- }
- }
-
- private static void ValidateUdn(SsdpDevice device, List<string> retVal)
- {
- if (!device.Udn.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
- retVal.Add("UDN must begin with uuid:. Correct format is uuid:<uuid>");
- else if (device.Udn.Substring(5).Trim() != device.Uuid)
- retVal.Add("UDN incorrect. Correct format is uuid:<uuid>");
- }
-
- private static void ValidateIcons(SsdpDevice device, List<string> retVal)
- {
- if (device.Icons.Any((di) => di.Url == null))
- retVal.Add("Device icon is missing URL.");
-
- if (device.Icons.Any((di) => String.IsNullOrEmpty(di.MimeType)))
- retVal.Add("Device icon is missing mime type.");
-
- if (device.Icons.Any((di) => di.Width <= 0 || di.Height <= 0))
- retVal.Add("Device icon has zero (or negative) height, width or both.");
-
- if (device.Icons.Any((di) => di.ColorDepth <= 0))
- retVal.Add("Device icon has zero (or negative) colordepth.");
- }
-
- private void ValidateChildDevices(SsdpDevice device, List<string> retVal)
- {
- foreach (var childDevice in device.Devices)
- {
- foreach (var validationError in this.GetValidationErrors(childDevice))
- {
- retVal.Add("Embedded Device : " + childDevice.Uuid + ": " + validationError);
- }
- }
- }
-
- private static bool IsOverLength(string value, int maxLength)
- {
- return !String.IsNullOrEmpty(value) && value.Length > maxLength;
- }
-
- #endregion
-
- }
-}
diff --git a/SharedVersion.cs b/SharedVersion.cs
index c683d0245..379b5b43c 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,3 +1,3 @@
using System.Reflection;
-[assembly: AssemblyVersion("3.2.50.0")]
+[assembly: AssemblyVersion("3.5.2.0")]
diff --git a/SocketHttpListener/Ext.cs b/SocketHttpListener/Ext.cs
index 87f0887ed..125775180 100644
--- a/SocketHttpListener/Ext.cs
+++ b/SocketHttpListener/Ext.cs
@@ -7,9 +7,8 @@ using System.Net;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
-using SocketHttpListener.Net;
-using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
+using WebSocketState = System.Net.WebSockets.WebSocketState;
namespace SocketHttpListener
{
@@ -129,7 +128,7 @@ namespace SocketHttpListener
internal static string CheckIfClosable(this WebSocketState state)
{
- return state == WebSocketState.Closing
+ return state == WebSocketState.CloseSent
? "While closing the WebSocket connection."
: state == WebSocketState.Closed
? "The WebSocket connection has already been closed."
@@ -140,7 +139,7 @@ namespace SocketHttpListener
{
return state == WebSocketState.Connecting
? "A WebSocket connection isn't established."
- : state == WebSocketState.Closing
+ : state == WebSocketState.CloseSent
? "While closing the WebSocket connection."
: state == WebSocketState.Closed
? "The WebSocket connection has already been closed."
@@ -154,20 +153,6 @@ namespace SocketHttpListener
: null;
}
- internal static string CheckIfValidSendData(this byte[] data)
- {
- return data == null
- ? "'data' must not be null."
- : null;
- }
-
- internal static string CheckIfValidSendData(this string data)
- {
- return data == null
- ? "'data' must not be null."
- : null;
- }
-
internal static Stream Compress(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.Deflate
@@ -632,24 +617,6 @@ namespace SocketHttpListener
}
/// <summary>
- /// Emits the specified <see cref="EventHandler"/> delegate if it isn't <see langword="null"/>.
- /// </summary>
- /// <param name="eventHandler">
- /// A <see cref="EventHandler"/> to emit.
- /// </param>
- /// <param name="sender">
- /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>.
- /// </param>
- /// <param name="e">
- /// A <see cref="EventArgs"/> that contains no event data.
- /// </param>
- public static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
- {
- if (eventHandler != null)
- eventHandler(sender, e);
- }
-
- /// <summary>
/// Emits the specified <c>EventHandler&lt;TEventArgs&gt;</c> delegate
/// if it isn't <see langword="null"/>.
/// </summary>
@@ -674,27 +641,6 @@ namespace SocketHttpListener
}
/// <summary>
- /// Gets the collection of the HTTP cookies from the specified HTTP <paramref name="headers"/>.
- /// </summary>
- /// <returns>
- /// A <see cref="CookieCollection"/> that receives a collection of the HTTP cookies.
- /// </returns>
- /// <param name="headers">
- /// A <see cref="QueryParamCollection"/> that contains a collection of the HTTP headers.
- /// </param>
- /// <param name="response">
- /// <c>true</c> if <paramref name="headers"/> is a collection of the response headers;
- /// otherwise, <c>false</c>.
- /// </param>
- public static CookieCollection GetCookies(this QueryParamCollection headers, bool response)
- {
- var name = response ? "Set-Cookie" : "Cookie";
- return headers == null || !headers.Contains(name)
- ? new CookieCollection()
- : CookieHelper.Parse(headers[name], response);
- }
-
- /// <summary>
/// Gets the description of the specified HTTP status <paramref name="code"/>.
/// </summary>
/// <returns>
@@ -709,52 +655,6 @@ namespace SocketHttpListener
}
/// <summary>
- /// Gets the name from the specified <see cref="string"/> that contains a pair of name and
- /// value separated by a separator string.
- /// </summary>
- /// <returns>
- /// A <see cref="string"/> that represents the name if any; otherwise, <c>null</c>.
- /// </returns>
- /// <param name="nameAndValue">
- /// A <see cref="string"/> that contains a pair of name and value separated by a separator
- /// string.
- /// </param>
- /// <param name="separator">
- /// A <see cref="string"/> that represents a separator string.
- /// </param>
- public static string GetName(this string nameAndValue, string separator)
- {
- return (nameAndValue != null && nameAndValue.Length > 0) &&
- (separator != null && separator.Length > 0)
- ? nameAndValue.GetNameInternal(separator)
- : null;
- }
-
- /// <summary>
- /// Gets the name and value from the specified <see cref="string"/> that contains a pair of
- /// name and value separated by a separator string.
- /// </summary>
- /// <returns>
- /// A <c>KeyValuePair&lt;string, string&gt;</c> that represents the name and value if any.
- /// </returns>
- /// <param name="nameAndValue">
- /// A <see cref="string"/> that contains a pair of name and value separated by a separator
- /// string.
- /// </param>
- /// <param name="separator">
- /// A <see cref="string"/> that represents a separator string.
- /// </param>
- public static KeyValuePair<string, string> GetNameAndValue(
- this string nameAndValue, string separator)
- {
- var name = nameAndValue.GetName(separator);
- var value = nameAndValue.GetValue(separator);
- return name != null
- ? new KeyValuePair<string, string>(name, value)
- : new KeyValuePair<string, string>(null, null);
- }
-
- /// <summary>
/// Gets the description of the specified HTTP status <paramref name="code"/>.
/// </summary>
/// <returns>
@@ -819,28 +719,6 @@ namespace SocketHttpListener
}
/// <summary>
- /// Gets the value from the specified <see cref="string"/> that contains a pair of name and
- /// value separated by a separator string.
- /// </summary>
- /// <returns>
- /// A <see cref="string"/> that represents the value if any; otherwise, <c>null</c>.
- /// </returns>
- /// <param name="nameAndValue">
- /// A <see cref="string"/> that contains a pair of name and value separated by a separator
- /// string.
- /// </param>
- /// <param name="separator">
- /// A <see cref="string"/> that represents a separator string.
- /// </param>
- public static string GetValue(this string nameAndValue, string separator)
- {
- return (nameAndValue != null && nameAndValue.Length > 0) &&
- (separator != null && separator.Length > 0)
- ? nameAndValue.GetValueInternal(separator)
- : null;
- }
-
- /// <summary>
/// Determines whether the specified <see cref="ByteOrder"/> is host
/// (this computer architecture) byte order.
/// </summary>
diff --git a/SocketHttpListener/HttpResponse.cs b/SocketHttpListener/HttpResponse.cs
index 5aca28c7c..154a3d8e9 100644
--- a/SocketHttpListener/HttpResponse.cs
+++ b/SocketHttpListener/HttpResponse.cs
@@ -7,6 +7,7 @@ using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
using HttpVersion = SocketHttpListener.Net.HttpVersion;
using System.Linq;
using MediaBrowser.Model.Services;
+using SocketHttpListener.Net;
namespace SocketHttpListener
{
@@ -51,10 +52,18 @@ namespace SocketHttpListener
{
get
{
- return Headers.GetCookies(true);
+ return GetCookies(Headers, true);
}
}
+ private CookieCollection GetCookies(QueryParamCollection headers, bool response)
+ {
+ var name = response ? "Set-Cookie" : "Cookie";
+ return headers == null || !headers.Contains(name)
+ ? new CookieCollection()
+ : CookieHelper.Parse(headers[name], response);
+ }
+
public bool IsProxyAuthenticationRequired
{
get
@@ -111,17 +120,6 @@ namespace SocketHttpListener
return res;
}
- internal static HttpResponse CreateWebSocketResponse()
- {
- var res = new HttpResponse(HttpStatusCode.SwitchingProtocols);
-
- var headers = res.Headers;
- headers["Upgrade"] = "websocket";
- headers["Connection"] = "Upgrade";
-
- return res;
- }
-
#endregion
#region Public Methods
diff --git a/SocketHttpListener/Net/AuthenticationTypes.cs b/SocketHttpListener/Net/AuthenticationTypes.cs
new file mode 100644
index 000000000..df6b9d576
--- /dev/null
+++ b/SocketHttpListener/Net/AuthenticationTypes.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SocketHttpListener.Net
+{
+ internal class AuthenticationTypes
+ {
+ internal const string NTLM = "NTLM";
+ internal const string Negotiate = "Negotiate";
+ internal const string Basic = "Basic";
+ }
+}
diff --git a/SocketHttpListener/Net/EndPointListener.cs b/SocketHttpListener/Net/EndPointListener.cs
deleted file mode 100644
index 48c0ae7cb..000000000
--- a/SocketHttpListener/Net/EndPointListener.cs
+++ /dev/null
@@ -1,433 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Security.Cryptography.X509Certificates;
-using System.Threading;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.System;
-using MediaBrowser.Model.Text;
-using SocketHttpListener.Primitives;
-using ProtocolType = MediaBrowser.Model.Net.ProtocolType;
-using SocketType = MediaBrowser.Model.Net.SocketType;
-
-namespace SocketHttpListener.Net
-{
- sealed class EndPointListener
- {
- HttpListener listener;
- IPEndPoint endpoint;
- Socket sock;
- Dictionary<ListenerPrefix, HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener>
- List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*'
- List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+'
- X509Certificate cert;
- bool secure;
- Dictionary<HttpConnection, HttpConnection> unregistered;
- private readonly ILogger _logger;
- private bool _closed;
- private bool _enableDualMode;
- private readonly ICryptoProvider _cryptoProvider;
- private readonly ISocketFactory _socketFactory;
- private readonly ITextEncoding _textEncoding;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
- private readonly IFileSystem _fileSystem;
- private readonly IEnvironmentInfo _environment;
-
- public EndPointListener(HttpListener listener, IPAddress addr, int port, bool secure, X509Certificate cert, ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
- {
- this.listener = listener;
- _logger = logger;
- _cryptoProvider = cryptoProvider;
- _socketFactory = socketFactory;
- _memoryStreamFactory = memoryStreamFactory;
- _textEncoding = textEncoding;
- _fileSystem = fileSystem;
- _environment = environment;
-
- this.secure = secure;
- this.cert = cert;
-
- _enableDualMode = addr.Equals(IPAddress.IPv6Any);
- endpoint = new IPEndPoint(addr, port);
-
- prefixes = new Dictionary<ListenerPrefix, HttpListener>();
- unregistered = new Dictionary<HttpConnection, HttpConnection>();
-
- CreateSocket();
- }
-
- internal HttpListener Listener
- {
- get
- {
- return listener;
- }
- }
-
- private void CreateSocket()
- {
- try
- {
- sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
- }
- catch (SocketCreateException ex)
- {
- if (_enableDualMode && endpoint.Address.Equals(IPAddress.IPv6Any) &&
- (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) ||
- // mono on bsd is throwing this
- string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase)))
- {
- endpoint = new IPEndPoint(IPAddress.Any, endpoint.Port);
- _enableDualMode = false;
- sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
- }
- else
- {
- throw;
- }
- }
-
- try
- {
- sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- }
- catch (SocketException)
- {
- // This is not supported on all operating systems (qnap)
- }
-
- sock.Bind(endpoint);
-
- // This is the number TcpListener uses.
- sock.Listen(2147483647);
-
- new SocketAcceptor(_logger, sock, ProcessAccept, () => _closed).StartAccept();
- _closed = false;
- }
-
- private Socket CreateSocket(AddressFamily addressFamily, bool dualMode)
- {
- try
- {
- var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
-
- if (dualMode)
- {
- socket.DualMode = true;
- }
-
- return socket;
- }
- catch (SocketException ex)
- {
- throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
- }
- catch (ArgumentException ex)
- {
- if (dualMode)
- {
- // Mono for BSD incorrectly throws ArgumentException instead of SocketException
- throw new SocketCreateException("AddressFamilyNotSupported", ex);
- }
- else
- {
- throw;
- }
- }
- }
-
- private async void ProcessAccept(Socket accepted)
- {
- try
- {
- var listener = this;
-
- if (listener.secure && listener.cert == null)
- {
- accepted.Close();
- return;
- }
-
- HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false);
-
- //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
- lock (listener.unregistered)
- {
- listener.unregistered[conn] = conn;
- }
- conn.BeginReadRequest();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in ProcessAccept", ex);
- }
- }
-
- internal void RemoveConnection(HttpConnection conn)
- {
- lock (unregistered)
- {
- unregistered.Remove(conn);
- }
- }
-
- public bool BindContext(HttpListenerContext context)
- {
- HttpListenerRequest req = context.Request;
- ListenerPrefix prefix;
- HttpListener listener = SearchListener(req.Url, out prefix);
- if (listener == null)
- return false;
-
- context.Connection.Prefix = prefix;
- return true;
- }
-
- public void UnbindContext(HttpListenerContext context)
- {
- if (context == null || context.Request == null)
- return;
-
- listener.UnregisterContext(context);
- }
-
- HttpListener SearchListener(Uri uri, out ListenerPrefix prefix)
- {
- prefix = null;
- if (uri == null)
- return null;
-
- string host = uri.Host;
- int port = uri.Port;
- string path = WebUtility.UrlDecode(uri.AbsolutePath);
- string path_slash = path[path.Length - 1] == '/' ? path : path + "/";
-
- HttpListener best_match = null;
- int best_length = -1;
-
- if (host != null && host != "")
- {
- var p_ro = prefixes;
- foreach (ListenerPrefix p in p_ro.Keys)
- {
- string ppath = p.Path;
- if (ppath.Length < best_length)
- continue;
-
- if (p.Host != host || p.Port != port)
- continue;
-
- if (path.StartsWith(ppath) || path_slash.StartsWith(ppath))
- {
- best_length = ppath.Length;
- best_match = (HttpListener)p_ro[p];
- prefix = p;
- }
- }
- if (best_length != -1)
- return best_match;
- }
-
- List<ListenerPrefix> list = unhandled;
- best_match = MatchFromList(host, path, list, out prefix);
- if (path != path_slash && best_match == null)
- best_match = MatchFromList(host, path_slash, list, out prefix);
- if (best_match != null)
- return best_match;
-
- list = all;
- best_match = MatchFromList(host, path, list, out prefix);
- if (path != path_slash && best_match == null)
- best_match = MatchFromList(host, path_slash, list, out prefix);
- if (best_match != null)
- return best_match;
-
- return null;
- }
-
- HttpListener MatchFromList(string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
- {
- prefix = null;
- if (list == null)
- return null;
-
- HttpListener best_match = null;
- int best_length = -1;
-
- foreach (ListenerPrefix p in list)
- {
- string ppath = p.Path;
- if (ppath.Length < best_length)
- continue;
-
- if (path.StartsWith(ppath))
- {
- best_length = ppath.Length;
- best_match = p.Listener;
- prefix = p;
- }
- }
-
- return best_match;
- }
-
- void AddSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
- {
- if (coll == null)
- return;
-
- foreach (ListenerPrefix p in coll)
- {
- if (p.Path == prefix.Path) //TODO: code
- throw new HttpListenerException(400, "Prefix already in use.");
- }
- coll.Add(prefix);
- }
-
- bool RemoveSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
- {
- if (coll == null)
- return false;
-
- int c = coll.Count;
- for (int i = 0; i < c; i++)
- {
- ListenerPrefix p = (ListenerPrefix)coll[i];
- if (p.Path == prefix.Path)
- {
- coll.RemoveAt(i);
- return true;
- }
- }
- return false;
- }
-
- void CheckIfRemove()
- {
- if (prefixes.Count > 0)
- return;
-
- List<ListenerPrefix> list = unhandled;
- if (list != null && list.Count > 0)
- return;
-
- list = all;
- if (list != null && list.Count > 0)
- return;
-
- EndPointManager.RemoveEndPoint(this, endpoint);
- }
-
- public void Close()
- {
- _closed = true;
- sock.Close();
- lock (unregistered)
- {
- //
- // Clone the list because RemoveConnection can be called from Close
- //
- var connections = new List<HttpConnection>(unregistered.Keys);
-
- foreach (HttpConnection c in connections)
- c.Close(true);
- unregistered.Clear();
- }
- }
-
- public void AddPrefix(ListenerPrefix prefix, HttpListener listener)
- {
- List<ListenerPrefix> current;
- List<ListenerPrefix> future;
- if (prefix.Host == "*")
- {
- do
- {
- current = unhandled;
- future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
- prefix.Listener = listener;
- AddSpecial(future, prefix);
- } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
- return;
- }
-
- if (prefix.Host == "+")
- {
- do
- {
- current = all;
- future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
- prefix.Listener = listener;
- AddSpecial(future, prefix);
- } while (Interlocked.CompareExchange(ref all, future, current) != current);
- return;
- }
-
- Dictionary<ListenerPrefix, HttpListener> prefs;
- Dictionary<ListenerPrefix, HttpListener> p2;
- do
- {
- prefs = prefixes;
- if (prefs.ContainsKey(prefix))
- {
- HttpListener other = (HttpListener)prefs[prefix];
- if (other != listener) // TODO: code.
- throw new HttpListenerException(400, "There's another listener for " + prefix);
- return;
- }
- p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
- p2[prefix] = listener;
- } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs);
- }
-
- public void RemovePrefix(ListenerPrefix prefix, HttpListener listener)
- {
- List<ListenerPrefix> current;
- List<ListenerPrefix> future;
- if (prefix.Host == "*")
- {
- do
- {
- current = unhandled;
- future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
- if (!RemoveSpecial(future, prefix))
- break; // Prefix not found
- } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
- CheckIfRemove();
- return;
- }
-
- if (prefix.Host == "+")
- {
- do
- {
- current = all;
- future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
- if (!RemoveSpecial(future, prefix))
- break; // Prefix not found
- } while (Interlocked.CompareExchange(ref all, future, current) != current);
- CheckIfRemove();
- return;
- }
-
- Dictionary<ListenerPrefix, HttpListener> prefs;
- Dictionary<ListenerPrefix, HttpListener> p2;
- do
- {
- prefs = prefixes;
- if (!prefs.ContainsKey(prefix))
- break;
-
- p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
- p2.Remove(prefix);
- } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs);
- CheckIfRemove();
- }
- }
-}
diff --git a/SocketHttpListener/Net/EndPointManager.cs b/SocketHttpListener/Net/EndPointManager.cs
deleted file mode 100644
index 557caa59a..000000000
--- a/SocketHttpListener/Net/EndPointManager.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Net;
-using System.Reflection;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using SocketHttpListener.Primitives;
-
-namespace SocketHttpListener.Net
-{
- sealed class EndPointManager
- {
- // Dictionary<IPAddress, Dictionary<int, EndPointListener>>
- static Dictionary<string, Dictionary<int, EndPointListener>> ip_to_endpoints = new Dictionary<string, Dictionary<int, EndPointListener>>();
-
- private EndPointManager()
- {
- }
-
- public static void AddListener(ILogger logger, HttpListener listener)
- {
- List<string> added = new List<string>();
- try
- {
- lock (ip_to_endpoints)
- {
- foreach (string prefix in listener.Prefixes)
- {
- AddPrefixInternal(logger, prefix, listener);
- added.Add(prefix);
- }
- }
- }
- catch
- {
- foreach (string prefix in added)
- {
- RemovePrefix(logger, prefix, listener);
- }
- throw;
- }
- }
-
- public static void AddPrefix(ILogger logger, string prefix, HttpListener listener)
- {
- lock (ip_to_endpoints)
- {
- AddPrefixInternal(logger, prefix, listener);
- }
- }
-
- static void AddPrefixInternal(ILogger logger, string p, HttpListener listener)
- {
- ListenerPrefix lp = new ListenerPrefix(p);
- if (lp.Path.IndexOf('%') != -1)
- throw new HttpListenerException(400, "Invalid path.");
-
- if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1) // TODO: Code?
- throw new HttpListenerException(400, "Invalid path.");
-
- // listens on all the interfaces if host name cannot be parsed by IPAddress.
- EndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure).Result;
- epl.AddPrefix(lp, listener);
- }
-
- private static IPAddress GetIpAnyAddress(HttpListener listener)
- {
- return listener.EnableDualMode ? IPAddress.IPv6Any : IPAddress.Any;
- }
-
- static async Task<EndPointListener> GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure)
- {
- var networkManager = listener.NetworkManager;
-
- IPAddress addr;
- if (host == "*" || host == "+")
- addr = GetIpAnyAddress(listener);
- else if (IPAddress.TryParse(host, out addr) == false)
- {
- try
- {
- var all = (await networkManager.GetHostAddressesAsync(host).ConfigureAwait(false));
-
- addr = (all.Length == 0 ? null : IPAddress.Parse(all[0].Address)) ??
- GetIpAnyAddress(listener);
- }
- catch
- {
- addr = GetIpAnyAddress(listener);
- }
- }
-
- Dictionary<int, EndPointListener> p = null; // Dictionary<int, EndPointListener>
- if (!ip_to_endpoints.TryGetValue(addr.ToString(), out p))
- {
- p = new Dictionary<int, EndPointListener>();
- ip_to_endpoints[addr.ToString()] = p;
- }
-
- EndPointListener epl = null;
- if (p.ContainsKey(port))
- {
- epl = (EndPointListener)p[port];
- }
- else
- {
- epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo);
- p[port] = epl;
- }
-
- return epl;
- }
-
- public static void RemoveEndPoint(EndPointListener epl, IPEndPoint ep)
- {
- lock (ip_to_endpoints)
- {
- // Dictionary<int, EndPointListener> p
- Dictionary<int, EndPointListener> p;
- if (ip_to_endpoints.TryGetValue(ep.Address.ToString(), out p))
- {
- p.Remove(ep.Port);
- if (p.Count == 0)
- {
- ip_to_endpoints.Remove(ep.Address.ToString());
- }
- }
- epl.Close();
- }
- }
-
- public static void RemoveListener(ILogger logger, HttpListener listener)
- {
- lock (ip_to_endpoints)
- {
- foreach (string prefix in listener.Prefixes)
- {
- RemovePrefixInternal(logger, prefix, listener);
- }
- }
- }
-
- public static void RemovePrefix(ILogger logger, string prefix, HttpListener listener)
- {
- lock (ip_to_endpoints)
- {
- RemovePrefixInternal(logger, prefix, listener);
- }
- }
-
- static void RemovePrefixInternal(ILogger logger, string prefix, HttpListener listener)
- {
- ListenerPrefix lp = new ListenerPrefix(prefix);
- if (lp.Path.IndexOf('%') != -1)
- return;
-
- if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1)
- return;
-
- EndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure).Result;
- epl.RemovePrefix(lp, listener);
- }
- }
-}
diff --git a/SocketHttpListener/Net/HttpConnection.cs b/SocketHttpListener/Net/HttpConnection.cs
index 05576ea1e..9b4fb8705 100644
--- a/SocketHttpListener/Net/HttpConnection.cs
+++ b/SocketHttpListener/Net/HttpConnection.cs
@@ -13,7 +13,9 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
+using System.Security.Authentication;
+using System.Threading;
namespace SocketHttpListener.Net
{
sealed class HttpConnection
@@ -22,7 +24,7 @@ namespace SocketHttpListener.Net
const int BufferSize = 8192;
Socket _socket;
Stream _stream;
- EndPointListener _epl;
+ HttpEndPointListener _epl;
MemoryStream _memoryStream;
byte[] _buffer;
HttpListenerContext _context;
@@ -34,21 +36,21 @@ namespace SocketHttpListener.Net
int _reuses;
bool _contextBound;
bool secure;
- int _timeout = 300000; // 90k ms for first request, 15k ms from then on
+ int _timeout = 90000; // 90k ms for first request, 15k ms from then on
+ private Timer _timer;
IPEndPoint local_ep;
HttpListener _lastListener;
- int[] client_cert_errors;
X509Certificate cert;
SslStream ssl_stream;
private readonly ILogger _logger;
private readonly ICryptoProvider _cryptoProvider;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly IStreamHelper _streamHelper;
private readonly ITextEncoding _textEncoding;
private readonly IFileSystem _fileSystem;
private readonly IEnvironmentInfo _environment;
- private HttpConnection(ILogger logger, Socket socket, EndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
+ public HttpConnection(ILogger logger, Socket socket, HttpEndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IStreamHelper streamHelper, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
{
_logger = logger;
this._socket = socket;
@@ -56,47 +58,37 @@ namespace SocketHttpListener.Net
this.secure = secure;
this.cert = cert;
_cryptoProvider = cryptoProvider;
- _memoryStreamFactory = memoryStreamFactory;
+ _streamHelper = streamHelper;
_textEncoding = textEncoding;
_fileSystem = fileSystem;
_environment = environment;
- }
- private async Task InitStream()
- {
if (secure == false)
{
_stream = new SocketStream(_socket, false);
}
else
{
- //ssl_stream = _epl.Listener.CreateSslStream(new NetworkStream(_socket, false), false, (t, c, ch, e) =>
- //{
- // if (c == null)
- // return true;
- // var c2 = c as X509Certificate2;
- // if (c2 == null)
- // c2 = new X509Certificate2(c.GetRawCertData());
- // client_cert = c2;
- // client_cert_errors = new int[] { (int)e };
- // return true;
- //});
- //_stream = ssl_stream.AuthenticatedStream;
-
- ssl_stream = new SslStream(new SocketStream(_socket, false), false);
- await ssl_stream.AuthenticateAsServerAsync(cert).ConfigureAwait(false);
- _stream = ssl_stream;
- }
- Init();
- }
+ ssl_stream = new SslStream(new SocketStream(_socket, false), false, (t, c, ch, e) =>
+ {
+ if (c == null)
+ {
+ return true;
+ }
- public static async Task<HttpConnection> Create(ILogger logger, Socket sock, EndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
- {
- var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, memoryStreamFactory, textEncoding, fileSystem, environment);
+ //var c2 = c as X509Certificate2;
+ //if (c2 == null)
+ //{
+ // c2 = new X509Certificate2(c.GetRawCertData());
+ //}
- await connection.InitStream().ConfigureAwait(false);
+ //_clientCert = c2;
+ //_clientCertErrors = new int[] { (int)e };
+ return true;
+ });
- return connection;
+ _stream = ssl_stream;
+ }
}
public Stream Stream
@@ -107,12 +99,27 @@ namespace SocketHttpListener.Net
}
}
- internal int[] ClientCertificateErrors
+ public async Task Init()
{
- get { return client_cert_errors; }
+ _timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
+
+ if (ssl_stream != null)
+ {
+ var enableAsync = true;
+ if (enableAsync)
+ {
+ await ssl_stream.AuthenticateAsServerAsync(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false).ConfigureAwait(false);
+ }
+ else
+ {
+ ssl_stream.AuthenticateAsServer(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false);
+ }
+ }
+
+ InitInternal();
}
- void Init()
+ private void InitInternal()
{
_contextBound = false;
_requestStream = null;
@@ -123,7 +130,7 @@ namespace SocketHttpListener.Net
_position = 0;
_inputState = InputState.RequestLine;
_lineState = LineState.None;
- _context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem);
+ _context = new HttpListenerContext(this, _textEncoding);
}
public bool IsClosed
@@ -164,6 +171,13 @@ namespace SocketHttpListener.Net
set { _prefix = value; }
}
+ private void OnTimeout(object unused)
+ {
+ //_logger.Info("HttpConnection timer fired");
+ CloseSocket();
+ Unbind();
+ }
+
public void BeginReadRequest()
{
if (_buffer == null)
@@ -211,7 +225,7 @@ namespace SocketHttpListener.Net
{
var supportsDirectSocketAccess = !_context.Response.SendChunked && !isExpect100Continue && !secure;
- _responseStream = new HttpResponseStream(_stream, _context.Response, false, _memoryStreamFactory, _socket, supportsDirectSocketAccess, _environment, _fileSystem, _logger);
+ _responseStream = new HttpResponseStream(_stream, _context.Response, false, _streamHelper, _socket, supportsDirectSocketAccess, _environment, _fileSystem, _logger);
}
return _responseStream;
}
@@ -503,14 +517,14 @@ namespace SocketHttpListener.Net
// Don't close. Keep working.
_reuses++;
Unbind();
- Init();
+ InitInternal();
BeginReadRequest();
return;
}
_reuses++;
Unbind();
- Init();
+ InitInternal();
BeginReadRequest();
return;
}
diff --git a/SocketHttpListener/Net/HttpEndPointListener.cs b/SocketHttpListener/Net/HttpEndPointListener.cs
new file mode 100644
index 000000000..254e76140
--- /dev/null
+++ b/SocketHttpListener/Net/HttpEndPointListener.cs
@@ -0,0 +1,539 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+using ProtocolType = MediaBrowser.Model.Net.ProtocolType;
+using SocketType = MediaBrowser.Model.Net.SocketType;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Net
+{
+ internal sealed class HttpEndPointListener
+ {
+ private HttpListener _listener;
+ private IPEndPoint _endpoint;
+ private Socket _socket;
+ private Dictionary<ListenerPrefix, HttpListener> _prefixes;
+ private List<ListenerPrefix> _unhandledPrefixes; // host = '*'
+ private List<ListenerPrefix> _allPrefixes; // host = '+'
+ private X509Certificate _cert;
+ private bool _secure;
+ private Dictionary<HttpConnection, HttpConnection> _unregisteredConnections;
+
+ private readonly ILogger _logger;
+ private bool _closed;
+ private bool _enableDualMode;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly ISocketFactory _socketFactory;
+ private readonly ITextEncoding _textEncoding;
+ private readonly IStreamHelper _streamHelper;
+ private readonly IFileSystem _fileSystem;
+ private readonly IEnvironmentInfo _environment;
+
+ public HttpEndPointListener(HttpListener listener, IPAddress addr, int port, bool secure, X509Certificate cert, ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, IStreamHelper streamHelper, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
+ {
+ this._listener = listener;
+ _logger = logger;
+ _cryptoProvider = cryptoProvider;
+ _socketFactory = socketFactory;
+ _streamHelper = streamHelper;
+ _textEncoding = textEncoding;
+ _fileSystem = fileSystem;
+ _environment = environment;
+
+ this._secure = secure;
+ this._cert = cert;
+
+ _enableDualMode = addr.Equals(IPAddress.IPv6Any);
+ _endpoint = new IPEndPoint(addr, port);
+
+ _prefixes = new Dictionary<ListenerPrefix, HttpListener>();
+ _unregisteredConnections = new Dictionary<HttpConnection, HttpConnection>();
+
+ CreateSocket();
+ }
+
+ internal HttpListener Listener
+ {
+ get
+ {
+ return _listener;
+ }
+ }
+
+ private void CreateSocket()
+ {
+ try
+ {
+ _socket = CreateSocket(_endpoint.Address.AddressFamily, _enableDualMode);
+ }
+ catch (SocketCreateException ex)
+ {
+ if (_enableDualMode && _endpoint.Address.Equals(IPAddress.IPv6Any) &&
+ (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) ||
+ // mono 4.8.1 and lower on bsd is throwing this
+ string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase) ||
+ // mono 5.2 on bsd is throwing this
+ string.Equals(ex.ErrorCode, "OperationNotSupported", StringComparison.OrdinalIgnoreCase)))
+ {
+ _endpoint = new IPEndPoint(IPAddress.Any, _endpoint.Port);
+ _enableDualMode = false;
+ _socket = CreateSocket(_endpoint.Address.AddressFamily, _enableDualMode);
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ try
+ {
+ _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ }
+ catch (SocketException)
+ {
+ // This is not supported on all operating systems (qnap)
+ }
+
+ _socket.Bind(_endpoint);
+
+ // This is the number TcpListener uses.
+ _socket.Listen(2147483647);
+
+ Accept();
+
+ _closed = false;
+ }
+
+ private void Accept()
+ {
+ var acceptEventArg = new SocketAsyncEventArgs();
+ acceptEventArg.UserToken = this;
+ acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAccept);
+
+ Accept(acceptEventArg);
+ }
+
+ private static void TryCloseAndDispose(Socket socket)
+ {
+ try
+ {
+ using (socket)
+ {
+ socket.Close();
+ }
+ }
+ catch
+ {
+
+ }
+ }
+
+ private static void TryClose(Socket socket)
+ {
+ try
+ {
+ socket.Close();
+ }
+ catch
+ {
+
+ }
+ }
+
+ private void Accept(SocketAsyncEventArgs acceptEventArg)
+ {
+ // acceptSocket must be cleared since the context object is being reused
+ acceptEventArg.AcceptSocket = null;
+
+ try
+ {
+ bool willRaiseEvent = _socket.AcceptAsync(acceptEventArg);
+
+ if (!willRaiseEvent)
+ {
+ ProcessAccept(acceptEventArg);
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ }
+ catch (Exception ex)
+ {
+ HttpEndPointListener epl = (HttpEndPointListener)acceptEventArg.UserToken;
+
+ epl._logger.ErrorException("Error in socket.AcceptAsync", ex);
+ }
+ }
+
+ // This method is the callback method associated with Socket.AcceptAsync
+ // operations and is invoked when an accept operation is complete
+ //
+ private static void OnAccept(object sender, SocketAsyncEventArgs e)
+ {
+ ProcessAccept(e);
+ }
+
+ private static async void ProcessAccept(SocketAsyncEventArgs args)
+ {
+ HttpEndPointListener epl = (HttpEndPointListener)args.UserToken;
+
+ if (epl._closed)
+ {
+ return;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/system.net.sockets.acceptSocket.acceptasync%28v=vs.110%29.aspx
+ // Under certain conditions ConnectionReset can occur
+ // Need to attept to re-accept
+ var socketError = args.SocketError;
+ var accepted = args.AcceptSocket;
+
+ epl.Accept(args);
+
+ if (socketError == SocketError.ConnectionReset)
+ {
+ epl._logger.Error("SocketError.ConnectionReset reported. Attempting to re-accept.");
+ return;
+ }
+
+ if(accepted == null)
+ {
+ return;
+ }
+
+ if (epl._secure && epl._cert == null)
+ {
+ TryClose(accepted);
+ return;
+ }
+
+ try
+ {
+ var remoteEndPointString = accepted.RemoteEndPoint == null ? string.Empty : accepted.RemoteEndPoint.ToString();
+ var localEndPointString = accepted.LocalEndPoint == null ? string.Empty : accepted.LocalEndPoint.ToString();
+ //_logger.Info("HttpEndPointListener Accepting connection from {0} to {1} secure connection requested: {2}", remoteEndPointString, localEndPointString, _secure);
+
+ HttpConnection conn = new HttpConnection(epl._logger, accepted, epl, epl._secure, epl._cert, epl._cryptoProvider, epl._streamHelper, epl._textEncoding, epl._fileSystem, epl._environment);
+
+ await conn.Init().ConfigureAwait(false);
+
+ //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
+ lock (epl._unregisteredConnections)
+ {
+ epl._unregisteredConnections[conn] = conn;
+ }
+ conn.BeginReadRequest();
+ }
+ catch (Exception ex)
+ {
+ epl._logger.ErrorException("Error in ProcessAccept", ex);
+
+ TryClose(accepted);
+ epl.Accept();
+ return;
+ }
+ }
+
+ private Socket CreateSocket(AddressFamily addressFamily, bool dualMode)
+ {
+ try
+ {
+ var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
+
+ if (dualMode)
+ {
+ socket.DualMode = true;
+ }
+
+ return socket;
+ }
+ catch (SocketException ex)
+ {
+ throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
+ }
+ catch (ArgumentException ex)
+ {
+ if (dualMode)
+ {
+ // Mono for BSD incorrectly throws ArgumentException instead of SocketException
+ throw new SocketCreateException("AddressFamilyNotSupported", ex);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ internal void RemoveConnection(HttpConnection conn)
+ {
+ lock (_unregisteredConnections)
+ {
+ _unregisteredConnections.Remove(conn);
+ }
+ }
+
+ public bool BindContext(HttpListenerContext context)
+ {
+ HttpListenerRequest req = context.Request;
+ ListenerPrefix prefix;
+ HttpListener listener = SearchListener(req.Url, out prefix);
+ if (listener == null)
+ return false;
+
+ context.Connection.Prefix = prefix;
+ return true;
+ }
+
+ public void UnbindContext(HttpListenerContext context)
+ {
+ if (context == null || context.Request == null)
+ return;
+
+ _listener.UnregisterContext(context);
+ }
+
+ private HttpListener SearchListener(Uri uri, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (uri == null)
+ return null;
+
+ string host = uri.Host;
+ int port = uri.Port;
+ string path = WebUtility.UrlDecode(uri.AbsolutePath);
+ string pathSlash = path[path.Length - 1] == '/' ? path : path + "/";
+
+ HttpListener bestMatch = null;
+ int bestLength = -1;
+
+ if (host != null && host != "")
+ {
+ Dictionary<ListenerPrefix, HttpListener> localPrefixes = _prefixes;
+ foreach (ListenerPrefix p in localPrefixes.Keys)
+ {
+ string ppath = p.Path;
+ if (ppath.Length < bestLength)
+ continue;
+
+ if (p.Host != host || p.Port != port)
+ continue;
+
+ if (path.StartsWith(ppath) || pathSlash.StartsWith(ppath))
+ {
+ bestLength = ppath.Length;
+ bestMatch = localPrefixes[p];
+ prefix = p;
+ }
+ }
+ if (bestLength != -1)
+ return bestMatch;
+ }
+
+ List<ListenerPrefix> list = _unhandledPrefixes;
+ bestMatch = MatchFromList(host, path, list, out prefix);
+
+ if (path != pathSlash && bestMatch == null)
+ bestMatch = MatchFromList(host, pathSlash, list, out prefix);
+
+ if (bestMatch != null)
+ return bestMatch;
+
+ list = _allPrefixes;
+ bestMatch = MatchFromList(host, path, list, out prefix);
+
+ if (path != pathSlash && bestMatch == null)
+ bestMatch = MatchFromList(host, pathSlash, list, out prefix);
+
+ if (bestMatch != null)
+ return bestMatch;
+
+ return null;
+ }
+
+ private HttpListener MatchFromList(string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (list == null)
+ return null;
+
+ HttpListener bestMatch = null;
+ int bestLength = -1;
+
+ foreach (ListenerPrefix p in list)
+ {
+ string ppath = p.Path;
+ if (ppath.Length < bestLength)
+ continue;
+
+ if (path.StartsWith(ppath))
+ {
+ bestLength = ppath.Length;
+ bestMatch = p._listener;
+ prefix = p;
+ }
+ }
+
+ return bestMatch;
+ }
+
+ private void AddSpecial(List<ListenerPrefix> list, ListenerPrefix prefix)
+ {
+ if (list == null)
+ return;
+
+ foreach (ListenerPrefix p in list)
+ {
+ if (p.Path == prefix.Path)
+ throw new Exception("net_listener_already");
+ }
+ list.Add(prefix);
+ }
+
+ private bool RemoveSpecial(List<ListenerPrefix> list, ListenerPrefix prefix)
+ {
+ if (list == null)
+ return false;
+
+ int c = list.Count;
+ for (int i = 0; i < c; i++)
+ {
+ ListenerPrefix p = list[i];
+ if (p.Path == prefix.Path)
+ {
+ list.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void CheckIfRemove()
+ {
+ if (_prefixes.Count > 0)
+ return;
+
+ List<ListenerPrefix> list = _unhandledPrefixes;
+ if (list != null && list.Count > 0)
+ return;
+
+ list = _allPrefixes;
+ if (list != null && list.Count > 0)
+ return;
+
+ HttpEndPointManager.RemoveEndPoint(this, _endpoint);
+ }
+
+ public void Close()
+ {
+ _closed = true;
+ _socket.Close();
+ lock (_unregisteredConnections)
+ {
+ // Clone the list because RemoveConnection can be called from Close
+ var connections = new List<HttpConnection>(_unregisteredConnections.Keys);
+
+ foreach (HttpConnection c in connections)
+ c.Close(true);
+ _unregisteredConnections.Clear();
+ }
+ }
+
+ public void AddPrefix(ListenerPrefix prefix, HttpListener listener)
+ {
+ List<ListenerPrefix> current;
+ List<ListenerPrefix> future;
+ if (prefix.Host == "*")
+ {
+ do
+ {
+ current = _unhandledPrefixes;
+ future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+ prefix._listener = listener;
+ AddSpecial(future, prefix);
+ } while (Interlocked.CompareExchange(ref _unhandledPrefixes, future, current) != current);
+ return;
+ }
+
+ if (prefix.Host == "+")
+ {
+ do
+ {
+ current = _allPrefixes;
+ future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+ prefix._listener = listener;
+ AddSpecial(future, prefix);
+ } while (Interlocked.CompareExchange(ref _allPrefixes, future, current) != current);
+ return;
+ }
+
+ Dictionary<ListenerPrefix, HttpListener> prefs, p2;
+ do
+ {
+ prefs = _prefixes;
+ if (prefs.ContainsKey(prefix))
+ {
+ throw new Exception("net_listener_already");
+ }
+ p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
+ p2[prefix] = listener;
+ } while (Interlocked.CompareExchange(ref _prefixes, p2, prefs) != prefs);
+ }
+
+ public void RemovePrefix(ListenerPrefix prefix, HttpListener listener)
+ {
+ List<ListenerPrefix> current;
+ List<ListenerPrefix> future;
+ if (prefix.Host == "*")
+ {
+ do
+ {
+ current = _unhandledPrefixes;
+ future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+ if (!RemoveSpecial(future, prefix))
+ break; // Prefix not found
+ } while (Interlocked.CompareExchange(ref _unhandledPrefixes, future, current) != current);
+
+ CheckIfRemove();
+ return;
+ }
+
+ if (prefix.Host == "+")
+ {
+ do
+ {
+ current = _allPrefixes;
+ future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+ if (!RemoveSpecial(future, prefix))
+ break; // Prefix not found
+ } while (Interlocked.CompareExchange(ref _allPrefixes, future, current) != current);
+ CheckIfRemove();
+ return;
+ }
+
+ Dictionary<ListenerPrefix, HttpListener> prefs, p2;
+ do
+ {
+ prefs = _prefixes;
+ if (!prefs.ContainsKey(prefix))
+ break;
+
+ p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
+ p2.Remove(prefix);
+ } while (Interlocked.CompareExchange(ref _prefixes, p2, prefs) != prefs);
+ CheckIfRemove();
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/HttpEndPointManager.cs b/SocketHttpListener/Net/HttpEndPointManager.cs
new file mode 100644
index 000000000..45af92c01
--- /dev/null
+++ b/SocketHttpListener/Net/HttpEndPointManager.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ internal sealed class HttpEndPointManager
+ {
+ private static Dictionary<IPAddress, Dictionary<int, HttpEndPointListener>> s_ipEndPoints = new Dictionary<IPAddress, Dictionary<int, HttpEndPointListener>>();
+
+ private HttpEndPointManager()
+ {
+ }
+
+ public static void AddListener(ILogger logger, HttpListener listener)
+ {
+ List<string> added = new List<string>();
+ try
+ {
+ lock ((s_ipEndPoints as ICollection).SyncRoot)
+ {
+ foreach (string prefix in listener.Prefixes)
+ {
+ AddPrefixInternal(logger, prefix, listener);
+ added.Add(prefix);
+ }
+ }
+ }
+ catch
+ {
+ foreach (string prefix in added)
+ {
+ RemovePrefix(logger, prefix, listener);
+ }
+ throw;
+ }
+ }
+
+ public static void AddPrefix(ILogger logger, string prefix, HttpListener listener)
+ {
+ lock ((s_ipEndPoints as ICollection).SyncRoot)
+ {
+ AddPrefixInternal(logger, prefix, listener);
+ }
+ }
+
+ private static void AddPrefixInternal(ILogger logger, string p, HttpListener listener)
+ {
+ int start = p.IndexOf(':') + 3;
+ int colon = p.IndexOf(':', start);
+ if (colon != -1)
+ {
+ // root can't be -1 here, since we've already checked for ending '/' in ListenerPrefix.
+ int root = p.IndexOf('/', colon, p.Length - colon);
+ string portString = p.Substring(colon + 1, root - colon - 1);
+
+ int port;
+ if (!int.TryParse(portString, out port) || port <= 0 || port >= 65536)
+ {
+ throw new HttpListenerException((int)HttpStatusCode.BadRequest, "net_invalid_port");
+ }
+ }
+
+ ListenerPrefix lp = new ListenerPrefix(p);
+ if (lp.Host != "*" && lp.Host != "+" && Uri.CheckHostName(lp.Host) == UriHostNameType.Unknown)
+ throw new HttpListenerException((int)HttpStatusCode.BadRequest, "net_listener_host");
+
+ if (lp.Path.IndexOf('%') != -1)
+ throw new HttpListenerException((int)HttpStatusCode.BadRequest, "net_invalid_path");
+
+ if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1)
+ throw new HttpListenerException((int)HttpStatusCode.BadRequest, "net_invalid_path");
+
+ // listens on all the interfaces if host name cannot be parsed by IPAddress.
+ HttpEndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure);
+ epl.AddPrefix(lp, listener);
+ }
+
+ private static IPAddress GetIpAnyAddress(HttpListener listener)
+ {
+ return listener.EnableDualMode ? IPAddress.IPv6Any : IPAddress.Any;
+ }
+
+ private static HttpEndPointListener GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure)
+ {
+ IPAddress addr;
+ if (host == "*" || host == "+")
+ {
+ addr = GetIpAnyAddress(listener);
+ }
+ else
+ {
+ const int NotSupportedErrorCode = 50;
+ try
+ {
+ addr = Dns.GetHostAddresses(host)[0];
+ }
+ catch
+ {
+ // Throw same error code as windows, request is not supported.
+ throw new HttpListenerException(NotSupportedErrorCode, "net_listener_not_supported");
+ }
+
+ if (IPAddress.Any.Equals(addr))
+ {
+ // Don't support listening to 0.0.0.0, match windows behavior.
+ throw new HttpListenerException(NotSupportedErrorCode, "net_listener_not_supported");
+ }
+ }
+
+ Dictionary<int, HttpEndPointListener> p = null;
+ if (s_ipEndPoints.ContainsKey(addr))
+ {
+ p = s_ipEndPoints[addr];
+ }
+ else
+ {
+ p = new Dictionary<int, HttpEndPointListener>();
+ s_ipEndPoints[addr] = p;
+ }
+
+ HttpEndPointListener epl = null;
+ if (p.ContainsKey(port))
+ {
+ epl = p[port];
+ }
+ else
+ {
+ try
+ {
+ epl = new HttpEndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.SocketFactory, listener.StreamHelper, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo);
+ }
+ catch (SocketException ex)
+ {
+ throw new HttpListenerException(ex.ErrorCode, ex.Message);
+ }
+ p[port] = epl;
+ }
+
+ return epl;
+ }
+
+ public static void RemoveEndPoint(HttpEndPointListener epl, IPEndPoint ep)
+ {
+ lock ((s_ipEndPoints as ICollection).SyncRoot)
+ {
+ Dictionary<int, HttpEndPointListener> p = null;
+ p = s_ipEndPoints[ep.Address];
+ p.Remove(ep.Port);
+ if (p.Count == 0)
+ {
+ s_ipEndPoints.Remove(ep.Address);
+ }
+ epl.Close();
+ }
+ }
+
+ public static void RemoveListener(ILogger logger, HttpListener listener)
+ {
+ lock ((s_ipEndPoints as ICollection).SyncRoot)
+ {
+ foreach (string prefix in listener.Prefixes)
+ {
+ RemovePrefixInternal(logger, prefix, listener);
+ }
+ }
+ }
+
+ public static void RemovePrefix(ILogger logger, string prefix, HttpListener listener)
+ {
+ lock ((s_ipEndPoints as ICollection).SyncRoot)
+ {
+ RemovePrefixInternal(logger, prefix, listener);
+ }
+ }
+
+ private static void RemovePrefixInternal(ILogger logger, string prefix, HttpListener listener)
+ {
+ ListenerPrefix lp = new ListenerPrefix(prefix);
+ if (lp.Path.IndexOf('%') != -1)
+ return;
+
+ if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1)
+ return;
+
+ HttpEndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure);
+ epl.RemovePrefix(lp, listener);
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/HttpKnownHeaderNames.cs b/SocketHttpListener/Net/HttpKnownHeaderNames.cs
new file mode 100644
index 000000000..ea4695850
--- /dev/null
+++ b/SocketHttpListener/Net/HttpKnownHeaderNames.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SocketHttpListener.Net
+{
+ internal static partial class HttpKnownHeaderNames
+ {
+ // When adding a new constant, add it to HttpKnownHeaderNames.TryGetHeaderName.cs as well.
+
+ public const string Accept = "Accept";
+ public const string AcceptCharset = "Accept-Charset";
+ public const string AcceptEncoding = "Accept-Encoding";
+ public const string AcceptLanguage = "Accept-Language";
+ public const string AcceptPatch = "Accept-Patch";
+ public const string AcceptRanges = "Accept-Ranges";
+ public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
+ public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
+ public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
+ public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
+ public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
+ public const string AccessControlMaxAge = "Access-Control-Max-Age";
+ public const string Age = "Age";
+ public const string Allow = "Allow";
+ public const string AltSvc = "Alt-Svc";
+ public const string Authorization = "Authorization";
+ public const string CacheControl = "Cache-Control";
+ public const string Connection = "Connection";
+ public const string ContentDisposition = "Content-Disposition";
+ public const string ContentEncoding = "Content-Encoding";
+ public const string ContentLanguage = "Content-Language";
+ public const string ContentLength = "Content-Length";
+ public const string ContentLocation = "Content-Location";
+ public const string ContentMD5 = "Content-MD5";
+ public const string ContentRange = "Content-Range";
+ public const string ContentSecurityPolicy = "Content-Security-Policy";
+ public const string ContentType = "Content-Type";
+ public const string Cookie = "Cookie";
+ public const string Cookie2 = "Cookie2";
+ public const string Date = "Date";
+ public const string ETag = "ETag";
+ public const string Expect = "Expect";
+ public const string Expires = "Expires";
+ public const string From = "From";
+ public const string Host = "Host";
+ public const string IfMatch = "If-Match";
+ public const string IfModifiedSince = "If-Modified-Since";
+ public const string IfNoneMatch = "If-None-Match";
+ public const string IfRange = "If-Range";
+ public const string IfUnmodifiedSince = "If-Unmodified-Since";
+ public const string KeepAlive = "Keep-Alive";
+ public const string LastModified = "Last-Modified";
+ public const string Link = "Link";
+ public const string Location = "Location";
+ public const string MaxForwards = "Max-Forwards";
+ public const string Origin = "Origin";
+ public const string P3P = "P3P";
+ public const string Pragma = "Pragma";
+ public const string ProxyAuthenticate = "Proxy-Authenticate";
+ public const string ProxyAuthorization = "Proxy-Authorization";
+ public const string ProxyConnection = "Proxy-Connection";
+ public const string PublicKeyPins = "Public-Key-Pins";
+ public const string Range = "Range";
+ public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
+ public const string RetryAfter = "Retry-After";
+ public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
+ public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
+ public const string SecWebSocketKey = "Sec-WebSocket-Key";
+ public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
+ public const string SecWebSocketVersion = "Sec-WebSocket-Version";
+ public const string Server = "Server";
+ public const string SetCookie = "Set-Cookie";
+ public const string SetCookie2 = "Set-Cookie2";
+ public const string StrictTransportSecurity = "Strict-Transport-Security";
+ public const string TE = "TE";
+ public const string TSV = "TSV";
+ public const string Trailer = "Trailer";
+ public const string TransferEncoding = "Transfer-Encoding";
+ public const string Upgrade = "Upgrade";
+ public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
+ public const string UserAgent = "User-Agent";
+ public const string Vary = "Vary";
+ public const string Via = "Via";
+ public const string WWWAuthenticate = "WWW-Authenticate";
+ public const string Warning = "Warning";
+ public const string XAspNetVersion = "X-AspNet-Version";
+ public const string XContentDuration = "X-Content-Duration";
+ public const string XContentTypeOptions = "X-Content-Type-Options";
+ public const string XFrameOptions = "X-Frame-Options";
+ public const string XMSEdgeRef = "X-MSEdge-Ref";
+ public const string XPoweredBy = "X-Powered-By";
+ public const string XRequestID = "X-Request-ID";
+ public const string XUACompatible = "X-UA-Compatible";
+ }
+}
diff --git a/SocketHttpListener/Net/HttpListener.cs b/SocketHttpListener/Net/HttpListener.cs
index 32c5e90e0..759be64c9 100644
--- a/SocketHttpListener/Net/HttpListener.cs
+++ b/SocketHttpListener/Net/HttpListener.cs
@@ -21,7 +21,7 @@ namespace SocketHttpListener.Net
internal ISocketFactory SocketFactory { get; private set; }
internal IFileSystem FileSystem { get; private set; }
internal ITextEncoding TextEncoding { get; private set; }
- internal IMemoryStreamFactory MemoryStreamFactory { get; private set; }
+ internal IStreamHelper StreamHelper { get; private set; }
internal INetworkManager NetworkManager { get; private set; }
internal IEnvironmentInfo EnvironmentInfo { get; private set; }
@@ -42,14 +42,14 @@ namespace SocketHttpListener.Net
public Action<HttpListenerContext> OnContext { get; set; }
- public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
+ public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IStreamHelper streamHelper, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
{
_logger = logger;
CryptoProvider = cryptoProvider;
SocketFactory = socketFactory;
NetworkManager = networkManager;
TextEncoding = textEncoding;
- MemoryStreamFactory = memoryStreamFactory;
+ StreamHelper = streamHelper;
FileSystem = fileSystem;
EnvironmentInfo = environmentInfo;
prefixes = new HttpListenerPrefixCollection(logger, this);
@@ -58,13 +58,13 @@ namespace SocketHttpListener.Net
auth_schemes = AuthenticationSchemes.Anonymous;
}
- public HttpListener(X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
- :this(new NullLogger(), certificate, cryptoProvider, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo)
+ public HttpListener(X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IStreamHelper streamHelper, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
+ :this(new NullLogger(), certificate, cryptoProvider, socketFactory, networkManager, textEncoding, streamHelper, fileSystem, environmentInfo)
{
}
- public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
- : this(logger, cryptoProvider, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo)
+ public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IStreamHelper streamHelper, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
+ : this(logger, cryptoProvider, socketFactory, networkManager, textEncoding, streamHelper, fileSystem, environmentInfo)
{
_certificate = certificate;
}
@@ -185,7 +185,7 @@ namespace SocketHttpListener.Net
void Close(bool force)
{
CheckDisposed();
- EndPointManager.RemoveListener(_logger, this);
+ HttpEndPointManager.RemoveListener(_logger, this);
Cleanup(force);
}
@@ -230,7 +230,7 @@ namespace SocketHttpListener.Net
if (listening)
return;
- EndPointManager.AddListener(_logger, this);
+ HttpEndPointManager.AddListener(_logger, this);
listening = true;
}
@@ -248,7 +248,6 @@ namespace SocketHttpListener.Net
Close(true); //TODO: Should we force here or not?
disposed = true;
- GC.SuppressFinalize(this);
}
internal void CheckDisposed()
diff --git a/SocketHttpListener/Net/HttpListenerContext.Managed.cs b/SocketHttpListener/Net/HttpListenerContext.Managed.cs
new file mode 100644
index 000000000..db795f742
--- /dev/null
+++ b/SocketHttpListener/Net/HttpListenerContext.Managed.cs
@@ -0,0 +1,100 @@
+using System.ComponentModel;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Net.WebSockets;
+
+namespace SocketHttpListener.Net
+{
+ public sealed unsafe partial class HttpListenerContext
+ {
+ private HttpConnection _connection;
+
+ internal HttpListenerContext(HttpConnection connection, ITextEncoding textEncoding)
+ {
+ _connection = connection;
+ _response = new HttpListenerResponse(this, textEncoding);
+ Request = new HttpListenerRequest(this);
+ ErrorStatus = 400;
+ }
+
+ internal int ErrorStatus { get; set; }
+
+ internal string ErrorMessage { get; set; }
+
+ internal bool HaveError => ErrorMessage != null;
+
+ internal HttpConnection Connection => _connection;
+
+ internal void ParseAuthentication(System.Net.AuthenticationSchemes expectedSchemes)
+ {
+ if (expectedSchemes == System.Net.AuthenticationSchemes.Anonymous)
+ return;
+
+ string header = Request.Headers["Authorization"];
+ if (string.IsNullOrEmpty(header))
+ return;
+
+ if (IsBasicHeader(header))
+ {
+ _user = ParseBasicAuthentication(header.Substring(AuthenticationTypes.Basic.Length + 1));
+ }
+ }
+
+ internal IPrincipal ParseBasicAuthentication(string authData) =>
+ TryParseBasicAuth(authData, out HttpStatusCode errorCode, out string username, out string password) ?
+ new GenericPrincipal(new HttpListenerBasicIdentity(username, password), Array.Empty<string>()) :
+ null;
+
+ internal static bool IsBasicHeader(string header) =>
+ header.Length >= 6 &&
+ header[5] == ' ' &&
+ string.Compare(header, 0, AuthenticationTypes.Basic, 0, 5, StringComparison.OrdinalIgnoreCase) == 0;
+
+ internal static bool TryParseBasicAuth(string headerValue, out HttpStatusCode errorCode, out string username, out string password)
+ {
+ errorCode = HttpStatusCode.OK;
+ username = password = null;
+ try
+ {
+ if (string.IsNullOrWhiteSpace(headerValue))
+ {
+ return false;
+ }
+
+ string authString = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue));
+ int colonPos = authString.IndexOf(':');
+ if (colonPos < 0)
+ {
+ // username must be at least 1 char
+ errorCode = HttpStatusCode.BadRequest;
+ return false;
+ }
+
+ username = authString.Substring(0, colonPos);
+ password = authString.Substring(colonPos + 1);
+ return true;
+ }
+ catch
+ {
+ errorCode = HttpStatusCode.InternalServerError;
+ return false;
+ }
+ }
+
+ public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval)
+ {
+ return HttpWebSocket.AcceptWebSocketAsyncCore(this, subProtocol, receiveBufferSize, keepAliveInterval);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment<byte> internalBuffer)
+ {
+ WebSocketValidate.ValidateArraySegment(internalBuffer, nameof(internalBuffer));
+ HttpWebSocket.ValidateOptions(subProtocol, receiveBufferSize, HttpWebSocket.MinSendBufferSize, keepAliveInterval);
+ return HttpWebSocket.AcceptWebSocketAsyncCore(this, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/HttpListenerContext.cs b/SocketHttpListener/Net/HttpListenerContext.cs
index 1bf39589d..f4679568a 100644
--- a/SocketHttpListener/Net/HttpListenerContext.cs
+++ b/SocketHttpListener/Net/HttpListenerContext.cs
@@ -7,145 +7,39 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Text;
using SocketHttpListener.Net.WebSockets;
using SocketHttpListener.Primitives;
+using System.Threading.Tasks;
namespace SocketHttpListener.Net
{
- public sealed class HttpListenerContext
+ public sealed unsafe partial class HttpListenerContext
{
- HttpListenerRequest request;
- HttpListenerResponse response;
- IPrincipal user;
- HttpConnection cnc;
- string error;
- int err_status = 400;
- private readonly ICryptoProvider _cryptoProvider;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
- private readonly ITextEncoding _textEncoding;
+ internal HttpListener _listener;
+ private HttpListenerResponse _response;
+ private IPrincipal _user;
- internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
- {
- this.cnc = cnc;
- _cryptoProvider = cryptoProvider;
- _memoryStreamFactory = memoryStreamFactory;
- _textEncoding = textEncoding;
- request = new HttpListenerRequest(this, _textEncoding);
- response = new HttpListenerResponse(this, _textEncoding);
- }
-
- internal int ErrorStatus
- {
- get { return err_status; }
- set { err_status = value; }
- }
-
- internal string ErrorMessage
- {
- get { return error; }
- set { error = value; }
- }
-
- internal bool HaveError
- {
- get { return (error != null); }
- }
+ public HttpListenerRequest Request { get; }
- internal HttpConnection Connection
- {
- get { return cnc; }
- }
+ public IPrincipal User => _user;
- public HttpListenerRequest Request
- {
- get { return request; }
- }
+ // This can be used to cache the results of HttpListener.AuthenticationSchemeSelectorDelegate.
+ internal AuthenticationSchemes AuthenticationSchemes { get; set; }
public HttpListenerResponse Response
{
- get { return response; }
- }
-
- public IPrincipal User
- {
- get { return user; }
- }
-
- internal void ParseAuthentication(AuthenticationSchemes expectedSchemes)
- {
- if (expectedSchemes == AuthenticationSchemes.Anonymous)
- return;
-
- // TODO: Handle NTLM/Digest modes
- string header = request.Headers["Authorization"];
- if (header == null || header.Length < 2)
- return;
-
- string[] authenticationData = header.Split(new char[] { ' ' }, 2);
- if (string.Equals(authenticationData[0], "basic", StringComparison.OrdinalIgnoreCase))
+ get
{
- user = ParseBasicAuthentication(authenticationData[1]);
+ return _response;
}
- // TODO: throw if malformed -> 400 bad request
}
- internal IPrincipal ParseBasicAuthentication(string authData)
+ public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol)
{
- try
- {
- // Basic AUTH Data is a formatted Base64 String
- //string domain = null;
- string user = null;
- string password = null;
- int pos = -1;
- var authDataBytes = Convert.FromBase64String(authData);
- string authString = _textEncoding.GetDefaultEncoding().GetString(authDataBytes, 0, authDataBytes.Length);
-
- // The format is DOMAIN\username:password
- // Domain is optional
-
- pos = authString.IndexOf(':');
-
- // parse the password off the end
- password = authString.Substring(pos + 1);
-
- // discard the password
- authString = authString.Substring(0, pos);
-
- // check if there is a domain
- pos = authString.IndexOf('\\');
-
- if (pos > 0)
- {
- //domain = authString.Substring (0, pos);
- user = authString.Substring(pos);
- }
- else
- {
- user = authString;
- }
-
- HttpListenerBasicIdentity identity = new HttpListenerBasicIdentity(user, password);
- // TODO: What are the roles MS sets
- return new GenericPrincipal(identity, new string[0]);
- }
- catch (Exception)
- {
- // Invalid auth data is swallowed silently
- return null;
- }
+ return AcceptWebSocketAsync(subProtocol, HttpWebSocket.DefaultReceiveBufferSize, WebSocket.DefaultKeepAliveInterval);
}
- public HttpListenerWebSocketContext AcceptWebSocket(string protocol)
+ public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol, TimeSpan keepAliveInterval)
{
- if (protocol != null)
- {
- if (protocol.Length == 0)
- throw new ArgumentException("An empty string.", "protocol");
-
- if (!protocol.IsToken())
- throw new ArgumentException("Contains an invalid character.", "protocol");
- }
-
- return new HttpListenerWebSocketContext(this, protocol, _cryptoProvider, _memoryStreamFactory);
+ return AcceptWebSocketAsync(subProtocol, HttpWebSocket.DefaultReceiveBufferSize, keepAliveInterval);
}
}
diff --git a/SocketHttpListener/Net/HttpListenerPrefixCollection.cs b/SocketHttpListener/Net/HttpListenerPrefixCollection.cs
index 0b05539ee..53efcb0fa 100644
--- a/SocketHttpListener/Net/HttpListenerPrefixCollection.cs
+++ b/SocketHttpListener/Net/HttpListenerPrefixCollection.cs
@@ -36,13 +36,13 @@ namespace SocketHttpListener.Net
public void Add(string uriPrefix)
{
listener.CheckDisposed();
- ListenerPrefix.CheckUri(uriPrefix);
+ //ListenerPrefix.CheckUri(uriPrefix);
if (prefixes.Contains(uriPrefix))
return;
prefixes.Add(uriPrefix);
if (listener.IsListening)
- EndPointManager.AddPrefix(_logger, uriPrefix, listener);
+ HttpEndPointManager.AddPrefix(_logger, uriPrefix, listener);
}
public void Clear()
@@ -50,7 +50,7 @@ namespace SocketHttpListener.Net
listener.CheckDisposed();
prefixes.Clear();
if (listener.IsListening)
- EndPointManager.RemoveListener(_logger, listener);
+ HttpEndPointManager.RemoveListener(_logger, listener);
}
public bool Contains(string uriPrefix)
@@ -89,7 +89,7 @@ namespace SocketHttpListener.Net
bool result = prefixes.Remove(uriPrefix);
if (result && listener.IsListening)
- EndPointManager.RemovePrefix(_logger, uriPrefix, listener);
+ HttpEndPointManager.RemovePrefix(_logger, uriPrefix, listener);
return result;
}
diff --git a/SocketHttpListener/Net/HttpListenerRequest.Managed.cs b/SocketHttpListener/Net/HttpListenerRequest.Managed.cs
new file mode 100644
index 000000000..47a6dfcfd
--- /dev/null
+++ b/SocketHttpListener/Net/HttpListenerRequest.Managed.cs
@@ -0,0 +1,330 @@
+using System;
+using System.Text;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Security.Authentication.ExtendedProtection;
+using System.Security.Cryptography.X509Certificates;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Text;
+
+namespace SocketHttpListener.Net
+{
+ public sealed partial class HttpListenerRequest
+ {
+ private long _contentLength;
+ private bool _clSet;
+ private WebHeaderCollection _headers;
+ private string _method;
+ private Stream _inputStream;
+ private HttpListenerContext _context;
+ private bool _isChunked;
+
+ private static byte[] s_100continue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
+
+ internal HttpListenerRequest(HttpListenerContext context)
+ {
+ _context = context;
+ _headers = new WebHeaderCollection();
+ _version = HttpVersion.Version10;
+ }
+
+ private static readonly char[] s_separators = new char[] { ' ' };
+
+ internal void SetRequestLine(string req)
+ {
+ string[] parts = req.Split(s_separators, 3);
+ if (parts.Length != 3)
+ {
+ _context.ErrorMessage = "Invalid request line (parts).";
+ return;
+ }
+
+ _method = parts[0];
+ foreach (char c in _method)
+ {
+ int ic = (int)c;
+
+ if ((ic >= 'A' && ic <= 'Z') ||
+ (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
+ c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
+ c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
+ c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
+ continue;
+
+ _context.ErrorMessage = "(Invalid verb)";
+ return;
+ }
+
+ _rawUrl = parts[1];
+ if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/"))
+ {
+ _context.ErrorMessage = "Invalid request line (version).";
+ return;
+ }
+
+ try
+ {
+ _version = new Version(parts[2].Substring(5));
+ }
+ catch
+ {
+ _context.ErrorMessage = "Invalid request line (version).";
+ return;
+ }
+
+ if (_version.Major < 1)
+ {
+ _context.ErrorMessage = "Invalid request line (version).";
+ return;
+ }
+ if (_version.Major > 1)
+ {
+ _context.ErrorStatus = (int)HttpStatusCode.HttpVersionNotSupported;
+ _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.HttpVersionNotSupported);
+ return;
+ }
+ }
+
+ private static bool MaybeUri(string s)
+ {
+ int p = s.IndexOf(':');
+ if (p == -1)
+ return false;
+
+ if (p >= 10)
+ return false;
+
+ return IsPredefinedScheme(s.Substring(0, p));
+ }
+
+ private static bool IsPredefinedScheme(string scheme)
+ {
+ if (scheme == null || scheme.Length < 3)
+ return false;
+
+ char c = scheme[0];
+ if (c == 'h')
+ return (scheme == UriScheme.Http || scheme == UriScheme.Https);
+ if (c == 'f')
+ return (scheme == UriScheme.File || scheme == UriScheme.Ftp);
+
+ if (c == 'n')
+ {
+ c = scheme[1];
+ if (c == 'e')
+ return (scheme == UriScheme.News || scheme == UriScheme.NetPipe || scheme == UriScheme.NetTcp);
+ if (scheme == UriScheme.Nntp)
+ return true;
+ return false;
+ }
+ if ((c == 'g' && scheme == UriScheme.Gopher) || (c == 'm' && scheme == UriScheme.Mailto))
+ return true;
+
+ return false;
+ }
+
+ internal void FinishInitialization()
+ {
+ string host = UserHostName;
+ if (_version > HttpVersion.Version10 && (host == null || host.Length == 0))
+ {
+ _context.ErrorMessage = "Invalid host name";
+ return;
+ }
+
+ string path;
+ Uri raw_uri = null;
+ if (MaybeUri(_rawUrl.ToLowerInvariant()) && Uri.TryCreate(_rawUrl, UriKind.Absolute, out raw_uri))
+ path = raw_uri.PathAndQuery;
+ else
+ path = _rawUrl;
+
+ if ((host == null || host.Length == 0))
+ host = UserHostAddress;
+
+ if (raw_uri != null)
+ host = raw_uri.Host;
+
+ int colon = host.IndexOf(']') == -1 ? host.IndexOf(':') : host.LastIndexOf(':');
+ if (colon >= 0)
+ host = host.Substring(0, colon);
+
+ string base_uri = string.Format("{0}://{1}:{2}", RequestScheme, host, LocalEndPoint.Port);
+
+ if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out _requestUri))
+ {
+ _context.ErrorMessage = System.Net.WebUtility.HtmlEncode("Invalid url: " + base_uri + path);
+ return;
+ }
+
+ _requestUri = HttpListenerRequestUriBuilder.GetRequestUri(_rawUrl, _requestUri.Scheme,
+ _requestUri.Authority, _requestUri.LocalPath, _requestUri.Query);
+
+ if (_version >= HttpVersion.Version11)
+ {
+ string t_encoding = Headers[HttpKnownHeaderNames.TransferEncoding];
+ _isChunked = (t_encoding != null && string.Equals(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase));
+ // 'identity' is not valid!
+ if (t_encoding != null && !_isChunked)
+ {
+ _context.Connection.SendError(null, 501);
+ return;
+ }
+ }
+
+ if (!_isChunked && !_clSet)
+ {
+ if (string.Equals(_method, "POST", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(_method, "PUT", StringComparison.OrdinalIgnoreCase))
+ {
+ _context.Connection.SendError(null, 411);
+ return;
+ }
+ }
+
+ if (String.Compare(Headers[HttpKnownHeaderNames.Expect], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ HttpResponseStream output = _context.Connection.GetResponseStream();
+ output.InternalWrite(s_100continue, 0, s_100continue.Length);
+ }
+ }
+
+ internal static string Unquote(String str)
+ {
+ int start = str.IndexOf('\"');
+ int end = str.LastIndexOf('\"');
+ if (start >= 0 && end >= 0)
+ str = str.Substring(start + 1, end - 1);
+ return str.Trim();
+ }
+
+ internal void AddHeader(string header)
+ {
+ int colon = header.IndexOf(':');
+ if (colon == -1 || colon == 0)
+ {
+ _context.ErrorMessage = HttpStatusDescription.Get(400);
+ _context.ErrorStatus = 400;
+ return;
+ }
+
+ string name = header.Substring(0, colon).Trim();
+ string val = header.Substring(colon + 1).Trim();
+ if (name.Equals("content-length", StringComparison.OrdinalIgnoreCase))
+ {
+ // To match Windows behavior:
+ // Content lengths >= 0 and <= long.MaxValue are accepted as is.
+ // Content lengths > long.MaxValue and <= ulong.MaxValue are treated as 0.
+ // Content lengths < 0 cause the requests to fail.
+ // Other input is a failure, too.
+ long parsedContentLength =
+ ulong.TryParse(val, out ulong parsedUlongContentLength) ? (parsedUlongContentLength <= long.MaxValue ? (long)parsedUlongContentLength : 0) :
+ long.Parse(val);
+ if (parsedContentLength < 0 || (_clSet && parsedContentLength != _contentLength))
+ {
+ _context.ErrorMessage = "Invalid Content-Length.";
+ }
+ else
+ {
+ _contentLength = parsedContentLength;
+ _clSet = true;
+ }
+ }
+ else if (name.Equals("transfer-encoding", StringComparison.OrdinalIgnoreCase))
+ {
+ if (Headers[HttpKnownHeaderNames.TransferEncoding] != null)
+ {
+ _context.ErrorStatus = (int)HttpStatusCode.NotImplemented;
+ _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.NotImplemented);
+ }
+ }
+
+ if (_context.ErrorMessage == null)
+ {
+ _headers.Set(name, val);
+ }
+ }
+
+ // returns true is the stream could be reused.
+ internal bool FlushInput()
+ {
+ if (!HasEntityBody)
+ return true;
+
+ int length = 2048;
+ if (_contentLength > 0)
+ length = (int)Math.Min(_contentLength, (long)length);
+
+ byte[] bytes = new byte[length];
+ while (true)
+ {
+ try
+ {
+ IAsyncResult ares = InputStream.BeginRead(bytes, 0, length, null, null);
+ if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne(1000))
+ return false;
+ if (InputStream.EndRead(ares) <= 0)
+ return true;
+ }
+ catch (ObjectDisposedException)
+ {
+ _inputStream = null;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+
+ public long ContentLength64
+ {
+ get
+ {
+ if (_isChunked)
+ _contentLength = -1;
+
+ return _contentLength;
+ }
+ }
+
+ public bool HasEntityBody => (_contentLength > 0 || _isChunked);
+
+ public QueryParamCollection Headers => _headers;
+
+ public string HttpMethod => _method;
+
+ public Stream InputStream
+ {
+ get
+ {
+ if (_inputStream == null)
+ {
+ if (_isChunked || _contentLength > 0)
+ _inputStream = _context.Connection.GetRequestStream(_isChunked, _contentLength);
+ else
+ _inputStream = Stream.Null;
+ }
+
+ return _inputStream;
+ }
+ }
+
+ public bool IsAuthenticated => false;
+
+ public bool IsSecureConnection => _context.Connection.IsSecure;
+
+ public System.Net.IPEndPoint LocalEndPoint => _context.Connection.LocalEndPoint;
+
+ public System.Net.IPEndPoint RemoteEndPoint => _context.Connection.RemoteEndPoint;
+
+ public Guid RequestTraceIdentifier { get; } = Guid.NewGuid();
+
+ public string ServiceName => null;
+
+ private Uri RequestUri => _requestUri;
+ private bool SupportsWebSockets => true;
+ }
+}
diff --git a/SocketHttpListener/Net/HttpListenerRequest.cs b/SocketHttpListener/Net/HttpListenerRequest.cs
index 5e391424f..1b369dfa8 100644
--- a/SocketHttpListener/Net/HttpListenerRequest.cs
+++ b/SocketHttpListener/Net/HttpListenerRequest.cs
@@ -10,653 +10,534 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
+using System.Collections.Generic;
+using SocketHttpListener.Net.WebSockets;
namespace SocketHttpListener.Net
{
- public sealed class HttpListenerRequest
+ public sealed unsafe partial class HttpListenerRequest
{
- string[] accept_types;
- Encoding content_encoding;
- long content_length;
- bool cl_set;
- CookieCollection cookies;
- WebHeaderCollection headers;
- string method;
- Stream input_stream;
- Version version;
- QueryParamCollection query_string; // check if null is ok, check if read-only, check case-sensitiveness
- string raw_url;
- Uri url;
- Uri referrer;
- string[] user_languages;
- HttpListenerContext context;
- bool is_chunked;
- bool ka_set;
- bool? _keepAlive;
-
- private readonly ITextEncoding _textEncoding;
-
- internal HttpListenerRequest(HttpListenerContext context, ITextEncoding textEncoding)
- {
- this.context = context;
- _textEncoding = textEncoding;
- headers = new WebHeaderCollection();
- version = HttpVersion.Version10;
- }
+ private CookieCollection _cookies;
+ private bool? _keepAlive;
+ private string _rawUrl;
+ private Uri _requestUri;
+ private Version _version;
- static char[] separators = new char[] { ' ' };
+ public string[] AcceptTypes => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.Accept]);
- internal void SetRequestLine(string req)
- {
- string[] parts = req.Split(separators, 3);
- if (parts.Length != 3)
- {
- context.ErrorMessage = "Invalid request line (parts).";
- return;
- }
+ public string[] UserLanguages => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.AcceptLanguage]);
- method = parts[0];
- foreach (char c in method)
- {
- int ic = (int)c;
-
- if ((ic >= 'A' && ic <= 'Z') ||
- (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
- c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
- c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
- c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
- continue;
-
- context.ErrorMessage = "(Invalid verb)";
- return;
- }
-
- raw_url = parts[1];
- if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/"))
- {
- context.ErrorMessage = "Invalid request line (version).";
- return;
- }
-
- try
- {
- version = new Version(parts[2].Substring(5));
- if (version.Major < 1)
- throw new Exception();
- }
- catch
- {
- context.ErrorMessage = "Invalid request line (version).";
- return;
- }
+ private CookieCollection ParseCookies(Uri uri, string setCookieHeader)
+ {
+ CookieCollection cookies = new CookieCollection();
+ return cookies;
}
- void CreateQueryString(string query)
+ public CookieCollection Cookies
{
- if (query == null || query.Length == 0)
+ get
{
- query_string = new QueryParamCollection();
- return;
+ if (_cookies == null)
+ {
+ string cookieString = Headers[HttpKnownHeaderNames.Cookie];
+ if (!string.IsNullOrEmpty(cookieString))
+ {
+ _cookies = ParseCookies(RequestUri, cookieString);
+ }
+ if (_cookies == null)
+ {
+ _cookies = new CookieCollection();
+ }
+ }
+ return _cookies;
}
+ }
- query_string = new QueryParamCollection();
- if (query[0] == '?')
- query = query.Substring(1);
- string[] components = query.Split('&');
- foreach (string kv in components)
+ public Encoding ContentEncoding
+ {
+ get
{
- int pos = kv.IndexOf('=');
- if (pos == -1)
+ if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
{
- query_string.Add(null, WebUtility.UrlDecode(kv));
+ string postDataCharset = Headers["x-up-devcap-post-charset"];
+ if (postDataCharset != null && postDataCharset.Length > 0)
+ {
+ try
+ {
+ return Encoding.GetEncoding(postDataCharset);
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
}
- else
+ if (HasEntityBody)
{
- string key = WebUtility.UrlDecode(kv.Substring(0, pos));
- string val = WebUtility.UrlDecode(kv.Substring(pos + 1));
-
- query_string.Add(key, val);
+ if (ContentType != null)
+ {
+ string charSet = Helpers.GetCharSetValueFromHeader(ContentType);
+ if (charSet != null)
+ {
+ try
+ {
+ return Encoding.GetEncoding(charSet);
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
}
+ return TextEncodingExtensions.GetDefaultEncoding();
}
}
- internal void FinishInitialization()
- {
- string host = UserHostName;
- if (version > HttpVersion.Version10 && (host == null || host.Length == 0))
- {
- context.ErrorMessage = "Invalid host name";
- return;
- }
-
- string path;
- Uri raw_uri = null;
- if (MaybeUri(raw_url.ToLowerInvariant()) && Uri.TryCreate(raw_url, UriKind.Absolute, out raw_uri))
- path = raw_uri.PathAndQuery;
- else
- path = raw_url;
-
- if ((host == null || host.Length == 0))
- host = UserHostAddress;
-
- if (raw_uri != null)
- host = raw_uri.Host;
-
- int colon = host.LastIndexOf(':');
- if (colon >= 0)
- host = host.Substring(0, colon);
+ public string ContentType => Headers[HttpKnownHeaderNames.ContentType];
- string base_uri = String.Format("{0}://{1}:{2}",
- (IsSecureConnection) ? (IsWebSocketRequest ? "wss" : "https") : (IsWebSocketRequest ? "ws" : "http"),
- host, LocalEndPoint.Port);
+ public bool IsLocal => LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
- if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out url))
- {
- context.ErrorMessage = WebUtility.HtmlEncode("Invalid url: " + base_uri + path);
- return; return;
- }
-
- CreateQueryString(url.Query);
-
- if (version >= HttpVersion.Version11)
+ public bool IsWebSocketRequest
+ {
+ get
{
- string t_encoding = Headers["Transfer-Encoding"];
- is_chunked = (t_encoding != null && String.Compare(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase) == 0);
- // 'identity' is not valid!
- if (t_encoding != null && !is_chunked)
+ if (!SupportsWebSockets)
{
- context.Connection.SendError(null, 501);
- return;
+ return false;
}
- }
- if (!is_chunked && !cl_set)
- {
- if (String.Compare(method, "POST", StringComparison.OrdinalIgnoreCase) == 0 ||
- String.Compare(method, "PUT", StringComparison.OrdinalIgnoreCase) == 0)
+ bool foundConnectionUpgradeHeader = false;
+ if (string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Upgrade]))
{
- context.Connection.SendError(null, 411);
- return;
+ return false;
}
- }
-
- if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
- {
- var output = (HttpResponseStream)context.Connection.GetResponseStream(true);
-
- var _100continue = _textEncoding.GetASCIIEncoding().GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
-
- output.InternalWrite(_100continue, 0, _100continue.Length);
- }
- }
-
- static bool MaybeUri(string s)
- {
- int p = s.IndexOf(':');
- if (p == -1)
- return false;
- if (p >= 10)
- return false;
-
- return IsPredefinedScheme(s.Substring(0, p));
- }
+ foreach (string connection in Headers.GetValues(HttpKnownHeaderNames.Connection))
+ {
+ if (string.Equals(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase))
+ {
+ foundConnectionUpgradeHeader = true;
+ break;
+ }
+ }
- //
- // Using a simple block of if's is twice as slow as the compiler generated
- // switch statement. But using this tuned code is faster than the
- // compiler generated code, with a million loops on x86-64:
- //
- // With "http": .10 vs .51 (first check)
- // with "https": .16 vs .51 (second check)
- // with "foo": .22 vs .31 (never found)
- // with "mailto": .12 vs .51 (last check)
- //
- //
- static bool IsPredefinedScheme(string scheme)
- {
- if (scheme == null || scheme.Length < 3)
- return false;
+ if (!foundConnectionUpgradeHeader)
+ {
+ return false;
+ }
- char c = scheme[0];
- if (c == 'h')
- return (scheme == "http" || scheme == "https");
- if (c == 'f')
- return (scheme == "file" || scheme == "ftp");
+ foreach (string upgrade in Headers.GetValues(HttpKnownHeaderNames.Upgrade))
+ {
+ if (string.Equals(upgrade, HttpWebSocket.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
- if (c == 'n')
- {
- c = scheme[1];
- if (c == 'e')
- return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp");
- if (scheme == "nntp")
- return true;
return false;
}
- if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto"))
- return true;
-
- return false;
}
- internal static string Unquote(String str)
- {
- int start = str.IndexOf('\"');
- int end = str.LastIndexOf('\"');
- if (start >= 0 && end >= 0)
- str = str.Substring(start + 1, end - 1);
- return str.Trim();
- }
-
- internal void AddHeader(string header)
+ public bool KeepAlive
{
- int colon = header.IndexOf(':');
- if (colon == -1 || colon == 0)
- {
- context.ErrorMessage = "Bad Request";
- context.ErrorStatus = 400;
- return;
- }
-
- string name = header.Substring(0, colon).Trim();
- string val = header.Substring(colon + 1).Trim();
- string lower = name.ToLowerInvariant();
- headers.SetInternal(name, val);
- switch (lower)
+ get
{
- case "accept-language":
- user_languages = val.Split(','); // yes, only split with a ','
- break;
- case "accept":
- accept_types = val.Split(','); // yes, only split with a ','
- break;
- case "content-length":
- try
- {
- //TODO: max. content_length?
- content_length = Int64.Parse(val.Trim());
- if (content_length < 0)
- context.ErrorMessage = "Invalid Content-Length.";
- cl_set = true;
- }
- catch
- {
- context.ErrorMessage = "Invalid Content-Length.";
- }
-
- break;
- case "content-type":
- {
- var contents = val.Split(';');
- foreach (var content in contents)
- {
- var tmp = content.Trim();
- if (tmp.StartsWith("charset"))
- {
- var charset = tmp.GetValue("=");
- if (charset != null && charset.Length > 0)
- {
- try
- {
-
- // Support upnp/dlna devices - CONTENT-TYPE: text/xml ; charset="utf-8"\r\n
- charset = charset.Trim('"');
- var index = charset.IndexOf('"');
- if (index != -1) charset = charset.Substring(0, index);
-
- content_encoding = Encoding.GetEncoding(charset);
- }
- catch
- {
- context.ErrorMessage = "Invalid Content-Type header: " + charset;
- }
- }
-
- break;
- }
- }
- }
- break;
- case "referer":
- try
- {
- referrer = new Uri(val);
- }
- catch
+ if (!_keepAlive.HasValue)
+ {
+ string header = Headers[HttpKnownHeaderNames.ProxyConnection];
+ if (string.IsNullOrEmpty(header))
{
- referrer = new Uri("http://someone.is.screwing.with.the.headers.com/");
+ header = Headers[HttpKnownHeaderNames.Connection];
}
- break;
- case "cookie":
- if (cookies == null)
- cookies = new CookieCollection();
-
- string[] cookieStrings = val.Split(new char[] { ',', ';' });
- Cookie current = null;
- int version = 0;
- foreach (string cookieString in cookieStrings)
+ if (string.IsNullOrEmpty(header))
{
- string str = cookieString.Trim();
- if (str.Length == 0)
- continue;
- if (str.StartsWith("$Version"))
- {
- version = Int32.Parse(Unquote(str.Substring(str.IndexOf('=') + 1)));
- }
- else if (str.StartsWith("$Path"))
- {
- if (current != null)
- current.Path = str.Substring(str.IndexOf('=') + 1).Trim();
- }
- else if (str.StartsWith("$Domain"))
- {
- if (current != null)
- current.Domain = str.Substring(str.IndexOf('=') + 1).Trim();
- }
- else if (str.StartsWith("$Port"))
+ if (ProtocolVersion >= HttpVersion.Version11)
{
- if (current != null)
- current.Port = str.Substring(str.IndexOf('=') + 1).Trim();
+ _keepAlive = true;
}
else
{
- if (current != null)
- {
- cookies.Add(current);
- }
- current = new Cookie();
- int idx = str.IndexOf('=');
- if (idx > 0)
- {
- current.Name = str.Substring(0, idx).Trim();
- current.Value = str.Substring(idx + 1).Trim();
- }
- else
- {
- current.Name = str.Trim();
- current.Value = String.Empty;
- }
- current.Version = version;
+ header = Headers[HttpKnownHeaderNames.KeepAlive];
+ _keepAlive = !string.IsNullOrEmpty(header);
}
}
- if (current != null)
- {
- cookies.Add(current);
- }
- break;
- }
- }
-
- // returns true is the stream could be reused.
- internal bool FlushInput()
- {
- if (!HasEntityBody)
- return true;
-
- int length = 2048;
- if (content_length > 0)
- length = (int)Math.Min(content_length, (long)length);
-
- byte[] bytes = new byte[length];
- while (true)
- {
- // TODO: test if MS has a timeout when doing this
- try
- {
- var task = InputStream.ReadAsync(bytes, 0, length);
- var result = Task.WaitAll(new [] { task }, 1000);
- if (!result)
- {
- return false;
- }
- if (task.Result <= 0)
+ else
{
- return true;
+ header = header.ToLower(CultureInfo.InvariantCulture);
+ _keepAlive =
+ header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
+ header.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
- catch (ObjectDisposedException e)
- {
- input_stream = null;
- return true;
- }
- catch
- {
- return false;
- }
- }
- }
- public string[] AcceptTypes
- {
- get { return accept_types; }
+ return _keepAlive.Value;
+ }
}
- public int ClientCertificateError
+ public QueryParamCollection QueryString
{
get
{
- HttpConnection cnc = context.Connection;
- //if (cnc.ClientCertificate == null)
- // throw new InvalidOperationException("No client certificate");
- //int[] errors = cnc.ClientCertificateErrors;
- //if (errors != null && errors.Length > 0)
- // return errors[0];
- return 0;
+ QueryParamCollection queryString = new QueryParamCollection();
+ Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
+ return queryString;
}
}
- public Encoding ContentEncoding
+ public string RawUrl => _rawUrl;
+
+ private string RequestScheme => IsSecureConnection ? UriScheme.Https : UriScheme.Http;
+
+ public string UserAgent => Headers[HttpKnownHeaderNames.UserAgent];
+
+ public string UserHostAddress => LocalEndPoint.ToString();
+
+ public string UserHostName => Headers[HttpKnownHeaderNames.Host];
+
+ public Uri UrlReferrer
{
get
{
- if (content_encoding == null)
- content_encoding = _textEncoding.GetDefaultEncoding();
- return content_encoding;
+ string referrer = Headers[HttpKnownHeaderNames.Referer];
+ if (referrer == null)
+ {
+ return null;
+ }
+
+ bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out Uri urlReferrer);
+ return success ? urlReferrer : null;
}
}
- public long ContentLength64
- {
- get { return is_chunked ? -1 : content_length; }
- }
+ public Uri Url => RequestUri;
- public string ContentType
- {
- get { return headers["content-type"]; }
- }
+ public Version ProtocolVersion => _version;
- public CookieCollection Cookies
+ private static class Helpers
{
- get
+ //
+ // Get attribute off header value
+ //
+ internal static string GetCharSetValueFromHeader(string headerValue)
{
- // TODO: check if the collection is read-only
- if (cookies == null)
- cookies = new CookieCollection();
- return cookies;
- }
- }
+ const string AttrName = "charset";
- public bool HasEntityBody
- {
- get { return (content_length > 0 || is_chunked); }
- }
+ if (headerValue == null)
+ return null;
- public QueryParamCollection Headers
- {
- get { return headers; }
- }
+ int l = headerValue.Length;
+ int k = AttrName.Length;
- public string HttpMethod
- {
- get { return method; }
- }
+ // find properly separated attribute name
+ int i = 1; // start searching from 1
- public Stream InputStream
- {
- get
- {
- if (input_stream == null)
+ while (i < l)
{
- if (is_chunked || content_length > 0)
- input_stream = context.Connection.GetRequestStream(is_chunked, content_length);
- else
- input_stream = Stream.Null;
+ i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, AttrName, i, CompareOptions.IgnoreCase);
+ if (i < 0)
+ break;
+ if (i + k >= l)
+ break;
+
+ char chPrev = headerValue[i - 1];
+ char chNext = headerValue[i + k];
+ if ((chPrev == ';' || chPrev == ',' || char.IsWhiteSpace(chPrev)) && (chNext == '=' || char.IsWhiteSpace(chNext)))
+ break;
+
+ i += k;
}
- return input_stream;
- }
- }
+ if (i < 0 || i >= l)
+ return null;
- public bool IsAuthenticated
- {
- get { return false; }
- }
+ // skip to '=' and the following whitespace
+ i += k;
+ while (i < l && char.IsWhiteSpace(headerValue[i]))
+ i++;
+ if (i >= l || headerValue[i] != '=')
+ return null;
+ i++;
+ while (i < l && char.IsWhiteSpace(headerValue[i]))
+ i++;
+ if (i >= l)
+ return null;
- public bool IsLocal
- {
- get
+ // parse the value
+ string attrValue = null;
+
+ int j;
+
+ if (i < l && headerValue[i] == '"')
+ {
+ if (i == l - 1)
+ return null;
+ j = headerValue.IndexOf('"', i + 1);
+ if (j < 0 || j == i + 1)
+ return null;
+
+ attrValue = headerValue.Substring(i + 1, j - i - 1).Trim();
+ }
+ else
+ {
+ for (j = i; j < l; j++)
+ {
+ if (headerValue[j] == ';')
+ break;
+ }
+
+ if (j == i)
+ return null;
+
+ attrValue = headerValue.Substring(i, j - i).Trim();
+ }
+
+ return attrValue;
+ }
+
+ internal static string[] ParseMultivalueHeader(string s)
{
- var remoteEndPoint = RemoteEndPoint;
+ if (s == null)
+ return null;
+
+ int l = s.Length;
+
+ // collect comma-separated values into list
+
+ List<string> values = new List<string>();
+ int i = 0;
+
+ while (i < l)
+ {
+ // find next ,
+ int ci = s.IndexOf(',', i);
+ if (ci < 0)
+ ci = l;
+
+ // append corresponding server value
+ values.Add(s.Substring(i, ci - i));
+
+ // move to next
+ i = ci + 1;
+
+ // skip leading space
+ if (i < l && s[i] == ' ')
+ i++;
+ }
- return remoteEndPoint.Address.Equals(IPAddress.Loopback) ||
- remoteEndPoint.Address.Equals(IPAddress.IPv6Loopback) ||
- LocalEndPoint.Address.Equals(remoteEndPoint.Address);
+ // return list as array of strings
+
+ int n = values.Count;
+ string[] strings;
+
+ // if n is 0 that means s was empty string
+
+ if (n == 0)
+ {
+ strings = new string[1];
+ strings[0] = string.Empty;
+ }
+ else
+ {
+ strings = new string[n];
+ values.CopyTo(0, strings, 0, n);
+ }
+ return strings;
}
- }
- public bool IsSecureConnection
- {
- get { return context.Connection.IsSecure; }
- }
- public bool KeepAlive
- {
- get
+ private static string UrlDecodeStringFromStringInternal(string s, Encoding e)
{
- if (!_keepAlive.HasValue)
+ int count = s.Length;
+ UrlDecoder helper = new UrlDecoder(count, e);
+
+ // go through the string's chars collapsing %XX and %uXXXX and
+ // appending each char as char, with exception of %XX constructs
+ // that are appended as bytes
+
+ for (int pos = 0; pos < count; pos++)
{
- string header = Headers["Proxy-Connection"];
- if (string.IsNullOrEmpty(header))
+ char ch = s[pos];
+
+ if (ch == '+')
{
- header = Headers["Connection"];
+ ch = ' ';
}
- if (string.IsNullOrEmpty(header))
+ else if (ch == '%' && pos < count - 2)
{
- if (ProtocolVersion >= HttpVersion.Version11)
+ if (s[pos + 1] == 'u' && pos < count - 5)
{
- _keepAlive = true;
+ int h1 = HexToInt(s[pos + 2]);
+ int h2 = HexToInt(s[pos + 3]);
+ int h3 = HexToInt(s[pos + 4]);
+ int h4 = HexToInt(s[pos + 5]);
+
+ if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0)
+ { // valid 4 hex chars
+ ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
+ pos += 5;
+
+ // only add as char
+ helper.AddChar(ch);
+ continue;
+ }
}
else
{
- header = Headers["Keep-Alive"];
- _keepAlive = !string.IsNullOrEmpty(header);
+ int h1 = HexToInt(s[pos + 1]);
+ int h2 = HexToInt(s[pos + 2]);
+
+ if (h1 >= 0 && h2 >= 0)
+ { // valid 2 hex chars
+ byte b = (byte)((h1 << 4) | h2);
+ pos += 2;
+
+ // don't add as char
+ helper.AddByte(b);
+ continue;
+ }
}
}
+
+ if ((ch & 0xFF80) == 0)
+ helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
else
- {
- header = header.ToLower(CultureInfo.InvariantCulture);
- _keepAlive =
- header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
- header.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) >= 0;
- }
+ helper.AddChar(ch);
}
- return _keepAlive.Value;
+ return helper.GetString();
}
- }
- public IPEndPoint LocalEndPoint
- {
- get { return context.Connection.LocalEndPoint; }
- }
+ private static int HexToInt(char h)
+ {
+ return (h >= '0' && h <= '9') ? h - '0' :
+ (h >= 'a' && h <= 'f') ? h - 'a' + 10 :
+ (h >= 'A' && h <= 'F') ? h - 'A' + 10 :
+ -1;
+ }
- public Version ProtocolVersion
- {
- get { return version; }
- }
+ private class UrlDecoder
+ {
+ private int _bufferSize;
- public QueryParamCollection QueryString
- {
- get { return query_string; }
- }
+ // Accumulate characters in a special array
+ private int _numChars;
+ private char[] _charBuffer;
- public string RawUrl
- {
- get { return raw_url; }
- }
+ // Accumulate bytes for decoding into characters in a special array
+ private int _numBytes;
+ private byte[] _byteBuffer;
- public IPEndPoint RemoteEndPoint
- {
- get { return context.Connection.RemoteEndPoint; }
- }
+ // Encoding to convert chars to bytes
+ private Encoding _encoding;
- public Guid RequestTraceIdentifier
- {
- get { return Guid.Empty; }
- }
+ private void FlushBytes()
+ {
+ if (_numBytes > 0)
+ {
+ _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
+ _numBytes = 0;
+ }
+ }
- public Uri Url
- {
- get { return url; }
- }
+ internal UrlDecoder(int bufferSize, Encoding encoding)
+ {
+ _bufferSize = bufferSize;
+ _encoding = encoding;
- public Uri UrlReferrer
- {
- get { return referrer; }
- }
+ _charBuffer = new char[bufferSize];
+ // byte buffer created on demand
+ }
- public string UserAgent
- {
- get { return headers["user-agent"]; }
- }
+ internal void AddChar(char ch)
+ {
+ if (_numBytes > 0)
+ FlushBytes();
- public string UserHostAddress
- {
- get { return LocalEndPoint.ToString(); }
- }
+ _charBuffer[_numChars++] = ch;
+ }
- public string UserHostName
- {
- get { return headers["host"]; }
- }
+ internal void AddByte(byte b)
+ {
+ {
+ if (_byteBuffer == null)
+ _byteBuffer = new byte[_bufferSize];
- public string[] UserLanguages
- {
- get { return user_languages; }
- }
+ _byteBuffer[_numBytes++] = b;
+ }
+ }
- public string ServiceName
- {
- get
- {
- return null;
+ internal string GetString()
+ {
+ if (_numBytes > 0)
+ FlushBytes();
+
+ if (_numChars > 0)
+ return new string(_charBuffer, 0, _numChars);
+ else
+ return string.Empty;
+ }
}
- }
- private bool _websocketRequestWasSet;
- private bool _websocketRequest;
- /// <summary>
- /// Gets a value indicating whether the request is a WebSocket connection request.
- /// </summary>
- /// <value>
- /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
- /// </value>
- public bool IsWebSocketRequest
- {
- get
+ internal static void FillFromString(QueryParamCollection nvc, string s, bool urlencoded, Encoding encoding)
{
- if (!_websocketRequestWasSet)
+ int l = (s != null) ? s.Length : 0;
+ int i = (s.Length > 0 && s[0] == '?') ? 1 : 0;
+
+ while (i < l)
{
- _websocketRequest = method == "GET" &&
- version > HttpVersion.Version10 &&
- headers.Contains("Upgrade", "websocket") &&
- headers.Contains("Connection", "Upgrade");
+ // find next & while noting first = on the way (and if there are more)
- _websocketRequestWasSet = true;
- }
+ int si = i;
+ int ti = -1;
- return _websocketRequest;
+ while (i < l)
+ {
+ char ch = s[i];
+
+ if (ch == '=')
+ {
+ if (ti < 0)
+ ti = i;
+ }
+ else if (ch == '&')
+ {
+ break;
+ }
+
+ i++;
+ }
+
+ // extract the name / value pair
+
+ string name = null;
+ string value = null;
+
+ if (ti >= 0)
+ {
+ name = s.Substring(si, ti - si);
+ value = s.Substring(ti + 1, i - ti - 1);
+ }
+ else
+ {
+ value = s.Substring(si, i - si);
+ }
+
+ // add name / value pair to the collection
+
+ if (urlencoded)
+ nvc.Add(
+ name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
+ UrlDecodeStringFromStringInternal(value, encoding));
+ else
+ nvc.Add(name, value);
+
+ // trailing '&'
+
+ if (i == l - 1 && s[i] == '&')
+ nvc.Add(null, "");
+
+ i++;
+ }
}
}
}
diff --git a/SocketHttpListener/Net/HttpListenerRequestUriBuilder.cs b/SocketHttpListener/Net/HttpListenerRequestUriBuilder.cs
new file mode 100644
index 000000000..e61bde32e
--- /dev/null
+++ b/SocketHttpListener/Net/HttpListenerRequestUriBuilder.cs
@@ -0,0 +1,445 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Globalization;
+
+namespace SocketHttpListener.Net
+{
+ // We don't use the cooked URL because http.sys unescapes all percent-encoded values. However,
+ // we also can't just use the raw Uri, since http.sys supports not only Utf-8, but also ANSI/DBCS and
+ // Unicode code points. System.Uri only supports Utf-8.
+ // The purpose of this class is to convert all ANSI, DBCS, and Unicode code points into percent encoded
+ // Utf-8 characters.
+ internal sealed class HttpListenerRequestUriBuilder
+ {
+ private static readonly Encoding s_utf8Encoding = new UTF8Encoding(false, true);
+ private static readonly Encoding s_ansiEncoding = Encoding.GetEncoding(0, new EncoderExceptionFallback(), new DecoderExceptionFallback());
+
+ private readonly string _rawUri;
+ private readonly string _cookedUriScheme;
+ private readonly string _cookedUriHost;
+ private readonly string _cookedUriPath;
+ private readonly string _cookedUriQuery;
+
+ // This field is used to build the final request Uri string from the Uri parts passed to the ctor.
+ private StringBuilder _requestUriString;
+
+ // The raw path is parsed by looping through all characters from left to right. 'rawOctets'
+ // is used to store consecutive percent encoded octets as actual byte values: e.g. for path /pa%C3%84th%2F/
+ // rawOctets will be set to { 0xC3, 0x84 } when we reach character 't' and it will be { 0x2F } when
+ // we reach the final '/'. I.e. after a sequence of percent encoded octets ends, we use rawOctets as
+ // input to the encoding and percent encode the resulting string into UTF-8 octets.
+ //
+ // When parsing ANSI (Latin 1) encoded path '/pa%C4th/', %C4 will be added to rawOctets and when
+ // we reach 't', the content of rawOctets { 0xC4 } will be fed into the ANSI encoding. The resulting
+ // string 'Ä' will be percent encoded into UTF-8 octets and appended to requestUriString. The final
+ // path will be '/pa%C3%84th/', where '%C3%84' is the UTF-8 percent encoded character 'Ä'.
+ private List<byte> _rawOctets;
+ private string _rawPath;
+
+ // Holds the final request Uri.
+ private Uri _requestUri;
+
+ private HttpListenerRequestUriBuilder(string rawUri, string cookedUriScheme, string cookedUriHost,
+ string cookedUriPath, string cookedUriQuery)
+ {
+ _rawUri = rawUri;
+ _cookedUriScheme = cookedUriScheme;
+ _cookedUriHost = cookedUriHost;
+ _cookedUriPath = AddSlashToAsteriskOnlyPath(cookedUriPath);
+ _cookedUriQuery = cookedUriQuery ?? string.Empty;
+ }
+
+ public static Uri GetRequestUri(string rawUri, string cookedUriScheme, string cookedUriHost,
+ string cookedUriPath, string cookedUriQuery)
+ {
+ HttpListenerRequestUriBuilder builder = new HttpListenerRequestUriBuilder(rawUri,
+ cookedUriScheme, cookedUriHost, cookedUriPath, cookedUriQuery);
+
+ return builder.Build();
+ }
+
+ private Uri Build()
+ {
+ BuildRequestUriUsingRawPath();
+
+ if (_requestUri == null)
+ {
+ BuildRequestUriUsingCookedPath();
+ }
+
+ return _requestUri;
+ }
+
+ private void BuildRequestUriUsingCookedPath()
+ {
+ bool isValid = Uri.TryCreate(_cookedUriScheme + Uri.SchemeDelimiter + _cookedUriHost + _cookedUriPath +
+ _cookedUriQuery, UriKind.Absolute, out _requestUri);
+
+ // Creating a Uri from the cooked Uri should really always work: If not, we log at least.
+ if (!isValid)
+ {
+ //if (NetEventSource.IsEnabled)
+ // NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_create_uri, _cookedUriScheme, _cookedUriHost, _cookedUriPath, _cookedUriQuery));
+ }
+ }
+
+ private void BuildRequestUriUsingRawPath()
+ {
+ bool isValid = false;
+
+ // Initialize 'rawPath' only if really needed; i.e. if we build the request Uri from the raw Uri.
+ _rawPath = GetPath(_rawUri);
+
+ // Try to check the raw path using first the primary encoding (according to http.sys settings);
+ // if it fails try the secondary encoding.
+ ParsingResult result = BuildRequestUriUsingRawPath(GetEncoding(EncodingType.Primary));
+ if (result == ParsingResult.EncodingError)
+ {
+ Encoding secondaryEncoding = GetEncoding(EncodingType.Secondary);
+ result = BuildRequestUriUsingRawPath(secondaryEncoding);
+ }
+ isValid = (result == ParsingResult.Success) ? true : false;
+
+ // Log that we weren't able to create a Uri from the raw string.
+ if (!isValid)
+ {
+ //if (NetEventSource.IsEnabled)
+ // NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_create_uri, _cookedUriScheme, _cookedUriHost, _rawPath, _cookedUriQuery));
+ }
+ }
+
+ private static Encoding GetEncoding(EncodingType type)
+ {
+ Debug.Assert((type == EncodingType.Primary) || (type == EncodingType.Secondary),
+ "Unknown 'EncodingType' value: " + type.ToString());
+
+ if (type == EncodingType.Secondary)
+ {
+ return s_ansiEncoding;
+ }
+ else
+ {
+ return s_utf8Encoding;
+ }
+ }
+
+ private ParsingResult BuildRequestUriUsingRawPath(Encoding encoding)
+ {
+ Debug.Assert(encoding != null, "'encoding' must be assigned.");
+ Debug.Assert(!string.IsNullOrEmpty(_rawPath), "'rawPath' must have at least one character.");
+
+ _rawOctets = new List<byte>();
+ _requestUriString = new StringBuilder();
+ _requestUriString.Append(_cookedUriScheme);
+ _requestUriString.Append(Uri.SchemeDelimiter);
+ _requestUriString.Append(_cookedUriHost);
+
+ ParsingResult result = ParseRawPath(encoding);
+ if (result == ParsingResult.Success)
+ {
+ _requestUriString.Append(_cookedUriQuery);
+
+ Debug.Assert(_rawOctets.Count == 0,
+ "Still raw octets left. They must be added to the result path.");
+
+ if (!Uri.TryCreate(_requestUriString.ToString(), UriKind.Absolute, out _requestUri))
+ {
+ // If we can't create a Uri from the string, this is an invalid string and it doesn't make
+ // sense to try another encoding.
+ result = ParsingResult.InvalidString;
+ }
+ }
+
+ if (result != ParsingResult.Success)
+ {
+ //if (NetEventSource.IsEnabled)
+ // NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_raw_path, _rawPath, encoding.EncodingName));
+ }
+
+ return result;
+ }
+
+ private ParsingResult ParseRawPath(Encoding encoding)
+ {
+ Debug.Assert(encoding != null, "'encoding' must be assigned.");
+
+ int index = 0;
+ char current = '\0';
+ while (index < _rawPath.Length)
+ {
+ current = _rawPath[index];
+ if (current == '%')
+ {
+ // Assert is enough, since http.sys accepted the request string already. This should never happen.
+ Debug.Assert(index + 2 < _rawPath.Length, "Expected >=2 characters after '%' (e.g. %2F)");
+
+ index++;
+ current = _rawPath[index];
+ if (current == 'u' || current == 'U')
+ {
+ // We found "%u" which means, we have a Unicode code point of the form "%uXXXX".
+ Debug.Assert(index + 4 < _rawPath.Length, "Expected >=4 characters after '%u' (e.g. %u0062)");
+
+ // Decode the content of rawOctets into percent encoded UTF-8 characters and append them
+ // to requestUriString.
+ if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+ {
+ return ParsingResult.EncodingError;
+ }
+ if (!AppendUnicodeCodePointValuePercentEncoded(_rawPath.Substring(index + 1, 4)))
+ {
+ return ParsingResult.InvalidString;
+ }
+ index += 5;
+ }
+ else
+ {
+ // We found '%', but not followed by 'u', i.e. we have a percent encoded octed: %XX
+ if (!AddPercentEncodedOctetToRawOctetsList(encoding, _rawPath.Substring(index, 2)))
+ {
+ return ParsingResult.InvalidString;
+ }
+ index += 2;
+ }
+ }
+ else
+ {
+ // We found a non-'%' character: decode the content of rawOctets into percent encoded
+ // UTF-8 characters and append it to the result.
+ if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+ {
+ return ParsingResult.EncodingError;
+ }
+ // Append the current character to the result.
+ _requestUriString.Append(current);
+ index++;
+ }
+ }
+
+ // if the raw path ends with a sequence of percent encoded octets, make sure those get added to the
+ // result (requestUriString).
+ if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+ {
+ return ParsingResult.EncodingError;
+ }
+
+ return ParsingResult.Success;
+ }
+
+ private bool AppendUnicodeCodePointValuePercentEncoded(string codePoint)
+ {
+ // http.sys only supports %uXXXX (4 hex-digits), even though unicode code points could have up to
+ // 6 hex digits. Therefore we parse always 4 characters after %u and convert them to an int.
+ int codePointValue;
+ if (!int.TryParse(codePoint, NumberStyles.HexNumber, null, out codePointValue))
+ {
+ //if (NetEventSource.IsEnabled)
+ // NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, codePoint));
+ return false;
+ }
+
+ string unicodeString = null;
+ try
+ {
+ unicodeString = char.ConvertFromUtf32(codePointValue);
+ AppendOctetsPercentEncoded(_requestUriString, s_utf8Encoding.GetBytes(unicodeString));
+
+ return true;
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ //if (NetEventSource.IsEnabled)
+ // NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, codePoint));
+ }
+ catch (EncoderFallbackException e)
+ {
+ // If utf8Encoding.GetBytes() fails
+ //if (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_to_utf8, unicodeString, e.Message));
+ }
+
+ return false;
+ }
+
+ private bool AddPercentEncodedOctetToRawOctetsList(Encoding encoding, string escapedCharacter)
+ {
+ byte encodedValue;
+ if (!byte.TryParse(escapedCharacter, NumberStyles.HexNumber, null, out encodedValue))
+ {
+ //if (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, escapedCharacter));
+ return false;
+ }
+
+ _rawOctets.Add(encodedValue);
+
+ return true;
+ }
+
+ private bool EmptyDecodeAndAppendRawOctetsList(Encoding encoding)
+ {
+ if (_rawOctets.Count == 0)
+ {
+ return true;
+ }
+
+ string decodedString = null;
+ try
+ {
+ // If the encoding can get a string out of the byte array, this is a valid string in the
+ // 'encoding' encoding.
+ decodedString = encoding.GetString(_rawOctets.ToArray());
+
+ if (encoding == s_utf8Encoding)
+ {
+ AppendOctetsPercentEncoded(_requestUriString, _rawOctets.ToArray());
+ }
+ else
+ {
+ AppendOctetsPercentEncoded(_requestUriString, s_utf8Encoding.GetBytes(decodedString));
+ }
+
+ _rawOctets.Clear();
+
+ return true;
+ }
+ catch (DecoderFallbackException e)
+ {
+ //if (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_bytes, GetOctetsAsString(_rawOctets), e.Message));
+ }
+ catch (EncoderFallbackException e)
+ {
+ // If utf8Encoding.GetBytes() fails
+ //if (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_to_utf8, decodedString, e.Message));
+ }
+
+ return false;
+ }
+
+ private static void AppendOctetsPercentEncoded(StringBuilder target, IEnumerable<byte> octets)
+ {
+ foreach (byte octet in octets)
+ {
+ target.Append('%');
+ target.Append(octet.ToString("X2", CultureInfo.InvariantCulture));
+ }
+ }
+
+ private static string GetOctetsAsString(IEnumerable<byte> octets)
+ {
+ StringBuilder octetString = new StringBuilder();
+
+ bool first = true;
+ foreach (byte octet in octets)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ octetString.Append(' ');
+ }
+ octetString.Append(octet.ToString("X2", CultureInfo.InvariantCulture));
+ }
+
+ return octetString.ToString();
+ }
+
+ private static string GetPath(string uriString)
+ {
+ Debug.Assert(uriString != null, "uriString must not be null");
+ Debug.Assert(uriString.Length > 0, "uriString must not be empty");
+
+ int pathStartIndex = 0;
+
+ // Perf. improvement: nearly all strings are relative Uris. So just look if the
+ // string starts with '/'. If so, we have a relative Uri and the path starts at position 0.
+ // (http.sys already trimmed leading whitespaces)
+ if (uriString[0] != '/')
+ {
+ // We can't check against cookedUriScheme, since http.sys allows for request http://myserver/ to
+ // use a request line 'GET https://myserver/' (note http vs. https). Therefore check if the
+ // Uri starts with either http:// or https://.
+ int authorityStartIndex = 0;
+ if (uriString.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
+ {
+ authorityStartIndex = 7;
+ }
+ else if (uriString.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
+ {
+ authorityStartIndex = 8;
+ }
+
+ if (authorityStartIndex > 0)
+ {
+ // we have an absolute Uri. Find out where the authority ends and the path begins.
+ // Note that Uris like "http://server?query=value/1/2" are invalid according to RFC2616
+ // and http.sys behavior: If the Uri contains a query, there must be at least one '/'
+ // between the authority and the '?' character: It's safe to just look for the first
+ // '/' after the authority to determine the beginning of the path.
+ pathStartIndex = uriString.IndexOf('/', authorityStartIndex);
+ if (pathStartIndex == -1)
+ {
+ // e.g. for request lines like: 'GET http://myserver' (no final '/')
+ pathStartIndex = uriString.Length;
+ }
+ }
+ else
+ {
+ // RFC2616: Request-URI = "*" | absoluteURI | abs_path | authority
+ // 'authority' can only be used with CONNECT which is never received by HttpListener.
+ // I.e. if we don't have an absolute path (must start with '/') and we don't have
+ // an absolute Uri (must start with http:// or https://), then 'uriString' must be '*'.
+ Debug.Assert((uriString.Length == 1) && (uriString[0] == '*'), "Unknown request Uri string format",
+ "Request Uri string is not an absolute Uri, absolute path, or '*': {0}", uriString);
+
+ // Should we ever get here, be consistent with 2.0/3.5 behavior: just add an initial
+ // slash to the string and treat it as a path:
+ uriString = "/" + uriString;
+ }
+ }
+
+ // Find end of path: The path is terminated by
+ // - the first '?' character
+ // - the first '#' character: This is never the case here, since http.sys won't accept
+ // Uris containing fragments. Also, RFC2616 doesn't allow fragments in request Uris.
+ // - end of Uri string
+ int queryIndex = uriString.IndexOf('?');
+ if (queryIndex == -1)
+ {
+ queryIndex = uriString.Length;
+ }
+
+ // will always return a != null string.
+ return AddSlashToAsteriskOnlyPath(uriString.Substring(pathStartIndex, queryIndex - pathStartIndex));
+ }
+
+ private static string AddSlashToAsteriskOnlyPath(string path)
+ {
+ Debug.Assert(path != null, "'path' must not be null");
+
+ // If a request like "OPTIONS * HTTP/1.1" is sent to the listener, then the request Uri
+ // should be "http[s]://server[:port]/*" to be compatible with pre-4.0 behavior.
+ if ((path.Length == 1) && (path[0] == '*'))
+ {
+ return "/*";
+ }
+
+ return path;
+ }
+
+ private enum ParsingResult
+ {
+ Success,
+ InvalidString,
+ EncodingError
+ }
+
+ private enum EncodingType
+ {
+ Primary,
+ Secondary
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/HttpResponseStream.Managed.cs b/SocketHttpListener/Net/HttpResponseStream.Managed.cs
index 2de3fbb94..1a8a195fb 100644
--- a/SocketHttpListener/Net/HttpResponseStream.Managed.cs
+++ b/SocketHttpListener/Net/HttpResponseStream.Managed.cs
@@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
namespace SocketHttpListener.Net
@@ -50,18 +49,18 @@ namespace SocketHttpListener.Net
private bool _ignore_errors;
private bool _trailer_sent;
private Stream _stream;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly IStreamHelper _streamHelper;
private readonly Socket _socket;
private readonly bool _supportsDirectSocketAccess;
private readonly IEnvironmentInfo _environment;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
- internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, Socket socket, bool supportsDirectSocketAccess, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger)
+ internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IStreamHelper streamHelper, Socket socket, bool supportsDirectSocketAccess, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger)
{
_response = response;
_ignore_errors = ignore_errors;
- _memoryStreamFactory = memoryStreamFactory;
+ _streamHelper = streamHelper;
_socket = socket;
_supportsDirectSocketAccess = supportsDirectSocketAccess;
_environment = environment;
@@ -136,7 +135,7 @@ namespace SocketHttpListener.Net
//{
// if (_response.HeadersSent)
// return null;
- // var ms = _memoryStreamFactory.CreateNew();
+ // var ms = CreateNew();
// _response.SendHeaders(closing, ms);
// return ms;
//}
@@ -287,64 +286,9 @@ namespace SocketHttpListener.Net
public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
- //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !_response.SendChunked)
- //{
- // return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken);
- //}
-
return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
}
- private readonly byte[] _emptyBuffer = new byte[] { };
- private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
- {
- var ms = GetHeaders(false);
-
- byte[] preBuffer;
- if (ms != null)
- {
- using (var msCopy = new MemoryStream())
- {
- ms.CopyTo(msCopy);
- preBuffer = msCopy.ToArray();
- }
- }
- else
- {
- return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
- }
-
- _stream.Flush();
-
- _logger.Info("Socket sending file {0}", path);
-
- var taskCompletion = new TaskCompletionSource<bool>();
-
- Action<IAsyncResult> callback = callbackResult =>
- {
- try
- {
- _socket.EndSendFile(callbackResult);
- taskCompletion.TrySetResult(true);
- }
- catch (Exception ex)
- {
- taskCompletion.TrySetException(ex);
- }
- };
-
- var result = _socket.BeginSendFile(path, preBuffer, _emptyBuffer, TransmitFileOptions.UseDefaultWorkerThread, new AsyncCallback(callback), null);
-
- if (result.CompletedSynchronously)
- {
- callback(result);
- }
-
- cancellationToken.Register(() => taskCompletion.TrySetCanceled());
-
- return taskCompletion.Task;
- }
-
const int StreamCopyToBufferSize = 81920;
private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
@@ -375,71 +319,11 @@ namespace SocketHttpListener.Net
if (count > 0)
{
- if (allowAsync)
- {
- await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- await CopyToInternalAsyncWithSyncRead(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
- }
+ await _streamHelper.CopyToAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
}
else
{
- if (allowAsync)
- {
- await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- fs.CopyTo(targetStream, StreamCopyToBufferSize);
- }
- }
- }
- }
-
- private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
- {
- var array = new byte[StreamCopyToBufferSize];
- int bytesRead;
-
- while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
- {
- var bytesToWrite = Math.Min(bytesRead, copyLength);
-
- if (bytesToWrite > 0)
- {
- await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
- }
-
- copyLength -= bytesToWrite;
-
- if (copyLength <= 0)
- {
- break;
- }
- }
- }
-
- private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
- {
- var array = new byte[StreamCopyToBufferSize];
- int bytesRead;
-
- while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
- {
- var bytesToWrite = Math.Min(bytesRead, copyLength);
-
- if (bytesToWrite > 0)
- {
- await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
- }
-
- copyLength -= bytesToWrite;
-
- if (copyLength <= 0)
- {
- break;
+ await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/SocketHttpListener/Net/ListenerPrefix.cs b/SocketHttpListener/Net/ListenerPrefix.cs
index 605b7b88c..99bb118e5 100644
--- a/SocketHttpListener/Net/ListenerPrefix.cs
+++ b/SocketHttpListener/Net/ListenerPrefix.cs
@@ -4,50 +4,50 @@ using MediaBrowser.Model.Net;
namespace SocketHttpListener.Net
{
- sealed class ListenerPrefix
+ internal sealed class ListenerPrefix
{
- string original;
- string host;
- ushort port;
- string path;
- bool secure;
- IPAddress[] addresses;
- public HttpListener Listener;
+ private string _original;
+ private string _host;
+ private ushort _port;
+ private string _path;
+ private bool _secure;
+ private IPAddress[] _addresses;
+ internal HttpListener _listener;
public ListenerPrefix(string prefix)
{
- this.original = prefix;
+ _original = prefix;
Parse(prefix);
}
public override string ToString()
{
- return original;
+ return _original;
}
public IPAddress[] Addresses
{
- get { return addresses; }
- set { addresses = value; }
+ get { return _addresses; }
+ set { _addresses = value; }
}
public bool Secure
{
- get { return secure; }
+ get { return _secure; }
}
public string Host
{
- get { return host; }
+ get { return _host; }
}
public int Port
{
- get { return (int)port; }
+ get { return _port; }
}
public string Path
{
- get { return path; }
+ get { return _path; }
}
// Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
@@ -57,92 +57,46 @@ namespace SocketHttpListener.Net
if (other == null)
return false;
- return (original == other.original);
+ return (_original == other._original);
}
public override int GetHashCode()
{
- return original.GetHashCode();
+ return _original.GetHashCode();
}
- void Parse(string uri)
+ private void Parse(string uri)
{
ushort default_port = 80;
if (uri.StartsWith("https://"))
{
default_port = 443;
- secure = true;
+ _secure = true;
}
int length = uri.Length;
int start_host = uri.IndexOf(':') + 3;
if (start_host >= length)
- throw new ArgumentException("No host specified.");
+ throw new ArgumentException("net_listener_host");
int colon = uri.IndexOf(':', start_host, length - start_host);
int root;
if (colon > 0)
{
- host = uri.Substring(start_host, colon - start_host);
+ _host = uri.Substring(start_host, colon - start_host);
root = uri.IndexOf('/', colon, length - colon);
- port = (ushort)Int32.Parse(uri.Substring(colon + 1, root - colon - 1));
- path = uri.Substring(root);
+ _port = (ushort)int.Parse(uri.Substring(colon + 1, root - colon - 1));
+ _path = uri.Substring(root);
}
else
{
root = uri.IndexOf('/', start_host, length - start_host);
- host = uri.Substring(start_host, root - start_host);
- port = default_port;
- path = uri.Substring(root);
+ _host = uri.Substring(start_host, root - start_host);
+ _port = default_port;
+ _path = uri.Substring(root);
}
- if (path.Length != 1)
- path = path.Substring(0, path.Length - 1);
- }
-
- public static void CheckUri(string uri)
- {
- if (uri == null)
- throw new ArgumentNullException("uriPrefix");
-
- if (!uri.StartsWith("http://") && !uri.StartsWith("https://"))
- throw new ArgumentException("Only 'http' and 'https' schemes are supported.");
-
- int length = uri.Length;
- int start_host = uri.IndexOf(':') + 3;
- if (start_host >= length)
- throw new ArgumentException("No host specified.");
-
- int colon = uri.IndexOf(':', start_host, length - start_host);
- if (start_host == colon)
- throw new ArgumentException("No host specified.");
-
- int root;
- if (colon > 0)
- {
- root = uri.IndexOf('/', colon, length - colon);
- if (root == -1)
- throw new ArgumentException("No path specified.");
-
- try
- {
- int p = Int32.Parse(uri.Substring(colon + 1, root - colon - 1));
- if (p <= 0 || p >= 65536)
- throw new Exception();
- }
- catch
- {
- throw new ArgumentException("Invalid port.");
- }
- }
- else
- {
- root = uri.IndexOf('/', start_host, length - start_host);
- if (root == -1)
- throw new ArgumentException("No path specified.");
- }
-
- if (uri[uri.Length - 1] != '/')
- throw new ArgumentException("The prefix must end with '/'");
+ if (_path.Length != 1)
+ _path = _path.Substring(0, _path.Length - 1);
}
}
}
diff --git a/SocketHttpListener/Net/SocketAcceptor.cs b/SocketHttpListener/Net/SocketAcceptor.cs
deleted file mode 100644
index 36332f52b..000000000
--- a/SocketHttpListener/Net/SocketAcceptor.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using System;
-using System.Net.Sockets;
-using MediaBrowser.Model.Logging;
-
-namespace SocketHttpListener.Net
-{
- public class SocketAcceptor
- {
- private readonly ILogger _logger;
- private readonly Socket _originalSocket;
- private readonly Func<bool> _isClosed;
- private readonly Action<Socket> _onAccept;
-
- public SocketAcceptor(ILogger logger, Socket originalSocket, Action<Socket> onAccept, Func<bool> isClosed)
- {
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
- if (originalSocket == null)
- {
- throw new ArgumentNullException("originalSocket");
- }
- if (onAccept == null)
- {
- throw new ArgumentNullException("onAccept");
- }
- if (isClosed == null)
- {
- throw new ArgumentNullException("isClosed");
- }
-
- _logger = logger;
- _originalSocket = originalSocket;
- _isClosed = isClosed;
- _onAccept = onAccept;
- }
-
- public void StartAccept()
- {
- Socket dummy = null;
- StartAccept(null, ref dummy);
- }
-
- public void StartAccept(SocketAsyncEventArgs acceptEventArg, ref Socket accepted)
- {
- if (acceptEventArg == null)
- {
- acceptEventArg = new SocketAsyncEventArgs();
- acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
- }
- else
- {
- // acceptSocket must be cleared since the context object is being reused
- acceptEventArg.AcceptSocket = null;
- }
-
- try
- {
- bool willRaiseEvent = _originalSocket.AcceptAsync(acceptEventArg);
-
- if (!willRaiseEvent)
- {
- ProcessAccept(acceptEventArg);
- }
- }
- catch (Exception ex)
- {
- if (accepted != null)
- {
- try
- {
-#if NET46
- accepted.Close();
-#else
- accepted.Dispose();
-#endif
- }
- catch
- {
- }
- accepted = null;
- }
- }
- }
-
- // This method is the callback method associated with Socket.AcceptAsync
- // operations and is invoked when an accept operation is complete
- //
- void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
- {
- ProcessAccept(e);
- }
-
- private void ProcessAccept(SocketAsyncEventArgs e)
- {
- if (_isClosed())
- {
- return;
- }
-
- // http://msdn.microsoft.com/en-us/library/system.net.sockets.acceptSocket.acceptasync%28v=vs.110%29.aspx
- // Under certain conditions ConnectionReset can occur
- // Need to attept to re-accept
- if (e.SocketError == SocketError.ConnectionReset)
- {
- _logger.Error("SocketError.ConnectionReset reported. Attempting to re-accept.");
- Socket dummy = null;
- StartAccept(e, ref dummy);
- return;
- }
-
- var acceptSocket = e.AcceptSocket;
- if (acceptSocket != null)
- {
- //ProcessAccept(acceptSocket);
- _onAccept(acceptSocket);
- }
-
- // Accept the next connection request
- StartAccept(e, ref acceptSocket);
- }
- }
-}
diff --git a/SocketHttpListener/Net/UriScheme.cs b/SocketHttpListener/Net/UriScheme.cs
index 35b01e0e5..732fc0e7d 100644
--- a/SocketHttpListener/Net/UriScheme.cs
+++ b/SocketHttpListener/Net/UriScheme.cs
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
-using System.Threading.Tasks;
namespace SocketHttpListener.Net
{
- internal class UriScheme
+ internal static class UriScheme
{
public const string File = "file";
public const string Ftp = "ftp";
diff --git a/SocketHttpListener/Net/WebHeaderCollection.cs b/SocketHttpListener/Net/WebHeaderCollection.cs
index d82dc6816..4bed81404 100644
--- a/SocketHttpListener/Net/WebHeaderCollection.cs
+++ b/SocketHttpListener/Net/WebHeaderCollection.cs
@@ -35,8 +35,6 @@ namespace SocketHttpListener.Net
};
static readonly Dictionary<string, HeaderInfo> headers;
- HeaderInfo? headerRestriction;
- HeaderInfo? headerConsistency;
static WebHeaderCollection()
{
@@ -108,7 +106,6 @@ namespace SocketHttpListener.Net
if (name == null)
throw new ArgumentNullException("name");
- ThrowIfRestricted(name);
this.AddWithoutValidate(name, value);
}
@@ -237,7 +234,6 @@ namespace SocketHttpListener.Net
if (!IsHeaderValue(value))
throw new ArgumentException("invalid header value");
- ThrowIfRestricted(name);
base.Set(name, value);
}
@@ -317,27 +313,6 @@ namespace SocketHttpListener.Net
}
}
- // Private Methods
-
- public override int Remove(string name)
- {
- ThrowIfRestricted(name);
- return base.Remove(name);
- }
-
- protected void ThrowIfRestricted(string headerName)
- {
- if (!headerRestriction.HasValue)
- return;
-
- HeaderInfo info;
- if (!headers.TryGetValue(headerName, out info))
- return;
-
- if ((info & headerRestriction.Value) != 0)
- throw new ArgumentException("This header must be modified with the appropriate property.");
- }
-
internal static bool IsMultiValue(string headerName)
{
if (headerName == null)
diff --git a/SocketHttpListener/Net/WebHeaderEncoding.cs b/SocketHttpListener/Net/WebHeaderEncoding.cs
index 4a080179e..7290bfc63 100644
--- a/SocketHttpListener/Net/WebHeaderEncoding.cs
+++ b/SocketHttpListener/Net/WebHeaderEncoding.cs
@@ -83,48 +83,5 @@ namespace SocketHttpListener.Net
}
return bytes;
}
-
- // The normal client header parser just casts bytes to chars (see GetString).
- // Check if those bytes were actually utf-8 instead of ASCII.
- // If not, just return the input value.
- internal static string DecodeUtf8FromString(string input)
- {
- if (string.IsNullOrWhiteSpace(input))
- {
- return input;
- }
-
- bool possibleUtf8 = false;
- for (int i = 0; i < input.Length; i++)
- {
- if (input[i] > (char)255)
- {
- return input; // This couldn't have come from the wire, someone assigned it directly.
- }
- else if (input[i] > (char)127)
- {
- possibleUtf8 = true;
- break;
- }
- }
- if (possibleUtf8)
- {
- byte[] rawBytes = new byte[input.Length];
- for (int i = 0; i < input.Length; i++)
- {
- if (input[i] > (char)255)
- {
- return input; // This couldn't have come from the wire, someone assigned it directly.
- }
- rawBytes[i] = (byte)input[i];
- }
- try
- {
- return s_utf8Decoder.GetString(rawBytes);
- }
- catch (ArgumentException) { } // Not actually Utf-8
- }
- return input;
- }
}
}
diff --git a/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs b/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs
index 803c67b83..49375678d 100644
--- a/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs
+++ b/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs
@@ -12,337 +12,87 @@ using SocketHttpListener.Primitives;
namespace SocketHttpListener.Net.WebSockets
{
- /// <summary>
- /// Provides the properties used to access the information in a WebSocket connection request
- /// received by the <see cref="HttpListener"/>.
- /// </summary>
- /// <remarks>
- /// </remarks>
public class HttpListenerWebSocketContext : WebSocketContext
{
- #region Private Fields
+ private readonly Uri _requestUri;
+ private readonly QueryParamCollection _headers;
+ private readonly CookieCollection _cookieCollection;
+ private readonly IPrincipal _user;
+ private readonly bool _isAuthenticated;
+ private readonly bool _isLocal;
+ private readonly bool _isSecureConnection;
- private HttpListenerContext _context;
- private WebSocket _websocket;
+ private readonly string _origin;
+ private readonly IEnumerable<string> _secWebSocketProtocols;
+ private readonly string _secWebSocketVersion;
+ private readonly string _secWebSocketKey;
- #endregion
-
- #region Internal Constructors
+ private readonly WebSocket _webSocket;
internal HttpListenerWebSocketContext(
- HttpListenerContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory)
+ Uri requestUri,
+ QueryParamCollection headers,
+ CookieCollection cookieCollection,
+ IPrincipal user,
+ bool isAuthenticated,
+ bool isLocal,
+ bool isSecureConnection,
+ string origin,
+ IEnumerable<string> secWebSocketProtocols,
+ string secWebSocketVersion,
+ string secWebSocketKey,
+ WebSocket webSocket)
{
- _context = context;
- _websocket = new WebSocket(this, protocol, cryptoProvider, memoryStreamFactory);
- }
-
- #endregion
+ _cookieCollection = new CookieCollection();
+ _cookieCollection.Add(cookieCollection);
- #region Internal Properties
+ //_headers = new NameValueCollection(headers);
+ _headers = headers;
+ _user = CopyPrincipal(user);
- internal Stream Stream
- {
- get
- {
- return _context.Connection.Stream;
- }
+ _requestUri = requestUri;
+ _isAuthenticated = isAuthenticated;
+ _isLocal = isLocal;
+ _isSecureConnection = isSecureConnection;
+ _origin = origin;
+ _secWebSocketProtocols = secWebSocketProtocols;
+ _secWebSocketVersion = secWebSocketVersion;
+ _secWebSocketKey = secWebSocketKey;
+ _webSocket = webSocket;
}
- #endregion
+ public override Uri RequestUri => _requestUri;
- #region Public Properties
+ public override QueryParamCollection Headers => _headers;
- /// <summary>
- /// Gets the HTTP cookies included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="System.Net.CookieCollection"/> that contains the cookies.
- /// </value>
- public override CookieCollection CookieCollection
- {
- get
- {
- return _context.Request.Cookies;
- }
- }
+ public override string Origin => _origin;
- /// <summary>
- /// Gets the HTTP headers included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="QueryParamCollection"/> that contains the headers.
- /// </value>
- public override QueryParamCollection Headers
- {
- get
- {
- return _context.Request.Headers;
- }
- }
+ public override IEnumerable<string> SecWebSocketProtocols => _secWebSocketProtocols;
- /// <summary>
- /// Gets the value of the Host header included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Host header.
- /// </value>
- public override string Host
- {
- get
- {
- return _context.Request.Headers["Host"];
- }
- }
+ public override string SecWebSocketVersion => _secWebSocketVersion;
- /// <summary>
- /// Gets a value indicating whether the client is authenticated.
- /// </summary>
- /// <value>
- /// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
- /// </value>
- public override bool IsAuthenticated
- {
- get
- {
- return _context.Request.IsAuthenticated;
- }
- }
+ public override string SecWebSocketKey => _secWebSocketKey;
- /// <summary>
- /// Gets a value indicating whether the client connected from the local computer.
- /// </summary>
- /// <value>
- /// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
- /// </value>
- public override bool IsLocal
- {
- get
- {
- return _context.Request.IsLocal;
- }
- }
+ public override CookieCollection CookieCollection => _cookieCollection;
- /// <summary>
- /// Gets a value indicating whether the WebSocket connection is secured.
- /// </summary>
- /// <value>
- /// <c>true</c> if the connection is secured; otherwise, <c>false</c>.
- /// </value>
- public override bool IsSecureConnection
- {
- get
- {
- return _context.Connection.IsSecure;
- }
- }
+ public override IPrincipal User => _user;
- /// <summary>
- /// Gets a value indicating whether the request is a WebSocket connection request.
- /// </summary>
- /// <value>
- /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
- /// </value>
- public override bool IsWebSocketRequest
- {
- get
- {
- return _context.Request.IsWebSocketRequest;
- }
- }
+ public override bool IsAuthenticated => _isAuthenticated;
- /// <summary>
- /// Gets the value of the Origin header included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Origin header.
- /// </value>
- public override string Origin
- {
- get
- {
- return _context.Request.Headers["Origin"];
- }
- }
+ public override bool IsLocal => _isLocal;
- /// <summary>
- /// Gets the query string included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="QueryParamCollection"/> that contains the query string parameters.
- /// </value>
- public override QueryParamCollection QueryString
- {
- get
- {
- return _context.Request.QueryString;
- }
- }
+ public override bool IsSecureConnection => _isSecureConnection;
- /// <summary>
- /// Gets the URI requested by the client.
- /// </summary>
- /// <value>
- /// A <see cref="Uri"/> that represents the requested URI.
- /// </value>
- public override Uri RequestUri
- {
- get
- {
- return _context.Request.Url;
- }
- }
+ public override WebSocket WebSocket => _webSocket;
- /// <summary>
- /// Gets the value of the Sec-WebSocket-Key header included in the request.
- /// </summary>
- /// <remarks>
- /// This property provides a part of the information used by the server to prove that it
- /// received a valid WebSocket connection request.
- /// </remarks>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key header.
- /// </value>
- public override string SecWebSocketKey
+ private static IPrincipal CopyPrincipal(IPrincipal user)
{
- get
+ if (user != null)
{
- return _context.Request.Headers["Sec-WebSocket-Key"];
+ throw new NotImplementedException();
}
- }
- /// <summary>
- /// Gets the values of the Sec-WebSocket-Protocol header included in the request.
- /// </summary>
- /// <remarks>
- /// This property represents the subprotocols requested by the client.
- /// </remarks>
- /// <value>
- /// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/> instance that provides
- /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol
- /// header.
- /// </value>
- public override IEnumerable<string> SecWebSocketProtocols
- {
- get
- {
- var protocols = _context.Request.Headers["Sec-WebSocket-Protocol"];
- if (protocols != null)
- foreach (var protocol in protocols.Split(','))
- yield return protocol.Trim();
- }
- }
-
- /// <summary>
- /// Gets the value of the Sec-WebSocket-Version header included in the request.
- /// </summary>
- /// <remarks>
- /// This property represents the WebSocket protocol version.
- /// </remarks>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Version header.
- /// </value>
- public override string SecWebSocketVersion
- {
- get
- {
- return _context.Request.Headers["Sec-WebSocket-Version"];
- }
- }
-
- /// <summary>
- /// Gets the server endpoint as an IP address and a port number.
- /// </summary>
- /// <value>
- /// </value>
- public override IPEndPoint ServerEndPoint
- {
- get
- {
- return _context.Connection.LocalEndPoint;
- }
- }
-
- /// <summary>
- /// Gets the client information (identity, authentication, and security roles).
- /// </summary>
- /// <value>
- /// A <see cref="IPrincipal"/> that represents the client information.
- /// </value>
- public override IPrincipal User
- {
- get
- {
- return _context.User;
- }
- }
-
- /// <summary>
- /// Gets the client endpoint as an IP address and a port number.
- /// </summary>
- /// <value>
- /// </value>
- public override IPEndPoint UserEndPoint
- {
- get
- {
- return _context.Connection.RemoteEndPoint;
- }
+ return null;
}
-
- /// <summary>
- /// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication
- /// between client and server.
- /// </summary>
- /// <value>
- /// A <see cref="SocketHttpListener.WebSocket"/>.
- /// </value>
- public override WebSocket WebSocket
- {
- get
- {
- return _websocket;
- }
- }
-
- #endregion
-
- #region Internal Methods
-
- internal void Close()
- {
- try
- {
- _context.Connection.Close(true);
- }
- catch
- {
- // catch errors sending the closing handshake
- }
- }
-
- internal void Close(HttpStatusCode code)
- {
- _context.Response.StatusCode = (int)code;
- _context.Response.OutputStream.Dispose();
- }
-
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Returns a <see cref="string"/> that represents the current
- /// <see cref="HttpListenerWebSocketContext"/>.
- /// </summary>
- /// <returns>
- /// A <see cref="string"/> that represents the current
- /// <see cref="HttpListenerWebSocketContext"/>.
- /// </returns>
- public override string ToString()
- {
- return _context.Request.ToString();
- }
-
- #endregion
}
}
diff --git a/SocketHttpListener/Net/WebSockets/HttpWebSocket.Managed.cs b/SocketHttpListener/Net/WebSockets/HttpWebSocket.Managed.cs
new file mode 100644
index 000000000..571e4bdba
--- /dev/null
+++ b/SocketHttpListener/Net/WebSockets/HttpWebSocket.Managed.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Net.WebSockets
+{
+ internal static partial class HttpWebSocket
+ {
+ private const string SupportedVersion = "13";
+
+ internal static async Task<HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context,
+ string subProtocol,
+ int receiveBufferSize,
+ TimeSpan keepAliveInterval,
+ ArraySegment<byte>? internalBuffer = null)
+ {
+ ValidateOptions(subProtocol, receiveBufferSize, MinSendBufferSize, keepAliveInterval);
+
+ // get property will create a new response if one doesn't exist.
+ HttpListenerResponse response = context.Response;
+ HttpListenerRequest request = context.Request;
+ ValidateWebSocketHeaders(context);
+
+ string secWebSocketVersion = request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
+
+ // Optional for non-browser client
+ string origin = request.Headers[HttpKnownHeaderNames.Origin];
+
+ string[] secWebSocketProtocols = null;
+ string outgoingSecWebSocketProtocolString;
+ bool shouldSendSecWebSocketProtocolHeader =
+ ProcessWebSocketProtocolHeader(
+ request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
+ subProtocol,
+ out outgoingSecWebSocketProtocolString);
+
+ if (shouldSendSecWebSocketProtocolHeader)
+ {
+ secWebSocketProtocols = new string[] { outgoingSecWebSocketProtocolString };
+ response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString);
+ }
+
+ // negotiate the websocket key return value
+ string secWebSocketKey = request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
+ string secWebSocketAccept = HttpWebSocket.GetSecWebSocketAcceptString(secWebSocketKey);
+
+ response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
+ response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketUpgradeToken);
+ response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
+
+ response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // HTTP 101
+ response.StatusDescription = HttpStatusDescription.Get(HttpStatusCode.SwitchingProtocols);
+
+ HttpResponseStream responseStream = response.OutputStream as HttpResponseStream;
+
+ // Send websocket handshake headers
+ await responseStream.WriteWebSocketHandshakeHeadersAsync().ConfigureAwait(false);
+
+ //WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer: true, subProtocol, keepAliveInterval);
+ WebSocket webSocket = new WebSocket(subProtocol);
+
+ HttpListenerWebSocketContext webSocketContext = new HttpListenerWebSocketContext(
+ request.Url,
+ request.Headers,
+ request.Cookies,
+ context.User,
+ request.IsAuthenticated,
+ request.IsLocal,
+ request.IsSecureConnection,
+ origin,
+ secWebSocketProtocols != null ? secWebSocketProtocols : Array.Empty<string>(),
+ secWebSocketVersion,
+ secWebSocketKey,
+ webSocket);
+
+ webSocket.SetContext(webSocketContext, context.Connection.Close, context.Connection.Stream);
+
+ return webSocketContext;
+ }
+
+ private const bool WebSocketsSupported = true;
+ }
+}
diff --git a/SocketHttpListener/Net/WebSockets/HttpWebSocket.cs b/SocketHttpListener/Net/WebSockets/HttpWebSocket.cs
new file mode 100644
index 000000000..9dc9143f8
--- /dev/null
+++ b/SocketHttpListener/Net/WebSockets/HttpWebSocket.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics.CodeAnalysis;
+using System.Security.Cryptography;
+using System.Threading;
+
+namespace SocketHttpListener.Net.WebSockets
+{
+ internal static partial class HttpWebSocket
+ {
+ internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ internal const string WebSocketUpgradeToken = "websocket";
+ internal const int DefaultReceiveBufferSize = 16 * 1024;
+ internal const int DefaultClientSendBufferSize = 16 * 1024;
+
+ [SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 used only for hashing purposes, not for crypto.")]
+ internal static string GetSecWebSocketAcceptString(string secWebSocketKey)
+ {
+ string retVal;
+
+ // SHA1 used only for hashing purposes, not for crypto. Check here for FIPS compat.
+ using (SHA1 sha1 = SHA1.Create())
+ {
+ string acceptString = string.Concat(secWebSocketKey, HttpWebSocket.SecWebSocketKeyGuid);
+ byte[] toHash = Encoding.UTF8.GetBytes(acceptString);
+ retVal = Convert.ToBase64String(sha1.ComputeHash(toHash));
+ }
+
+ return retVal;
+ }
+
+ // return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
+ internal static bool ProcessWebSocketProtocolHeader(string clientSecWebSocketProtocol,
+ string subProtocol,
+ out string acceptProtocol)
+ {
+ acceptProtocol = string.Empty;
+ if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
+ {
+ // client hasn't specified any Sec-WebSocket-Protocol header
+ if (subProtocol != null)
+ {
+ // If the server specified _anything_ this isn't valid.
+ throw new WebSocketException("UnsupportedProtocol");
+ }
+ // Treat empty and null from the server as the same thing here, server should not send headers.
+ return false;
+ }
+
+ // here, we know the client specified something and it's non-empty.
+
+ if (subProtocol == null)
+ {
+ // client specified some protocols, server specified 'null'. So server should send headers.
+ return true;
+ }
+
+ // here, we know that the client has specified something, it's not empty
+ // and the server has specified exactly one protocol
+
+ string[] requestProtocols = clientSecWebSocketProtocol.Split(new char[] { ',' },
+ StringSplitOptions.RemoveEmptyEntries);
+ acceptProtocol = subProtocol;
+
+ // client specified protocols, serverOptions has exactly 1 non-empty entry. Check that
+ // this exists in the list the client specified.
+ for (int i = 0; i < requestProtocols.Length; i++)
+ {
+ string currentRequestProtocol = requestProtocols[i].Trim();
+ if (string.Equals(acceptProtocol, currentRequestProtocol, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ throw new WebSocketException("net_WebSockets_AcceptUnsupportedProtocol");
+ }
+
+ internal static void ValidateOptions(string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval)
+ {
+ if (subProtocol != null)
+ {
+ WebSocketValidate.ValidateSubprotocol(subProtocol);
+ }
+
+ if (receiveBufferSize < MinReceiveBufferSize)
+ {
+ throw new ArgumentOutOfRangeException("net_WebSockets_ArgumentOutOfRange_TooSmall");
+ }
+
+ if (sendBufferSize < MinSendBufferSize)
+ {
+ throw new ArgumentOutOfRangeException("net_WebSockets_ArgumentOutOfRange_TooSmall");
+ }
+
+ if (receiveBufferSize > MaxBufferSize)
+ {
+ throw new ArgumentOutOfRangeException("net_WebSockets_ArgumentOutOfRange_TooBig");
+ }
+
+ if (sendBufferSize > MaxBufferSize)
+ {
+ throw new ArgumentOutOfRangeException("net_WebSockets_ArgumentOutOfRange_TooBig");
+ }
+
+ if (keepAliveInterval < Timeout.InfiniteTimeSpan) // -1 millisecond
+ {
+ throw new ArgumentOutOfRangeException("net_WebSockets_ArgumentOutOfRange_TooSmall");
+ }
+ }
+
+ internal const int MinSendBufferSize = 16;
+ internal const int MinReceiveBufferSize = 256;
+ internal const int MaxBufferSize = 64 * 1024;
+
+ private static void ValidateWebSocketHeaders(HttpListenerContext context)
+ {
+ if (!WebSocketsSupported)
+ {
+ throw new PlatformNotSupportedException("net_WebSockets_UnsupportedPlatform");
+ }
+
+ if (!context.Request.IsWebSocketRequest)
+ {
+ throw new WebSocketException("net_WebSockets_AcceptNotAWebSocket");
+ }
+
+ string secWebSocketVersion = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
+ if (string.IsNullOrEmpty(secWebSocketVersion))
+ {
+ throw new WebSocketException("net_WebSockets_AcceptHeaderNotFound");
+ }
+
+ if (!string.Equals(secWebSocketVersion, SupportedVersion, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new WebSocketException("net_WebSockets_AcceptUnsupportedWebSocketVersion");
+ }
+
+ string secWebSocketKey = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
+ bool isSecWebSocketKeyInvalid = string.IsNullOrWhiteSpace(secWebSocketKey);
+ if (!isSecWebSocketKeyInvalid)
+ {
+ try
+ {
+ // key must be 16 bytes then base64-encoded
+ isSecWebSocketKeyInvalid = Convert.FromBase64String(secWebSocketKey).Length != 16;
+ }
+ catch
+ {
+ isSecWebSocketKeyInvalid = true;
+ }
+ }
+ if (isSecWebSocketKeyInvalid)
+ {
+ throw new WebSocketException("net_WebSockets_AcceptHeaderNotFound");
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/WebSockets/WebSocketCloseStatus.cs b/SocketHttpListener/Net/WebSockets/WebSocketCloseStatus.cs
new file mode 100644
index 000000000..0f43b7b80
--- /dev/null
+++ b/SocketHttpListener/Net/WebSockets/WebSocketCloseStatus.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SocketHttpListener.Net.WebSockets
+{
+ public enum WebSocketCloseStatus
+ {
+ NormalClosure = 1000,
+ EndpointUnavailable = 1001,
+ ProtocolError = 1002,
+ InvalidMessageType = 1003,
+ Empty = 1005,
+ // AbnormalClosure = 1006, // 1006 is reserved and should never be used by user
+ InvalidPayloadData = 1007,
+ PolicyViolation = 1008,
+ MessageTooBig = 1009,
+ MandatoryExtension = 1010,
+ InternalServerError = 1011
+ // TLSHandshakeFailed = 1015, // 1015 is reserved and should never be used by user
+
+ // 0 - 999 Status codes in the range 0-999 are not used.
+ // 1000 - 1999 Status codes in the range 1000-1999 are reserved for definition by this protocol.
+ // 2000 - 2999 Status codes in the range 2000-2999 are reserved for use by extensions.
+ // 3000 - 3999 Status codes in the range 3000-3999 MAY be used by libraries and frameworks. The
+ // interpretation of these codes is undefined by this protocol. End applications MUST
+ // NOT use status codes in this range.
+ // 4000 - 4999 Status codes in the range 4000-4999 MAY be used by application code. The interpretation
+ // of these codes is undefined by this protocol.
+ }
+}
diff --git a/SocketHttpListener/Net/WebSockets/WebSocketContext.cs b/SocketHttpListener/Net/WebSockets/WebSocketContext.cs
index 9665ab789..071b5fe05 100644
--- a/SocketHttpListener/Net/WebSockets/WebSocketContext.cs
+++ b/SocketHttpListener/Net/WebSockets/WebSocketContext.cs
@@ -8,176 +8,19 @@ using MediaBrowser.Model.Services;
namespace SocketHttpListener.Net.WebSockets
{
- /// <summary>
- /// Exposes the properties used to access the information in a WebSocket connection request.
- /// </summary>
- /// <remarks>
- /// The WebSocketContext class is an abstract class.
- /// </remarks>
public abstract class WebSocketContext
{
- #region Protected Constructors
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WebSocketContext"/> class.
- /// </summary>
- protected WebSocketContext()
- {
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Gets the HTTP cookies included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="System.Net.CookieCollection"/> that contains the cookies.
- /// </value>
- public abstract CookieCollection CookieCollection { get; }
-
- /// <summary>
- /// Gets the HTTP headers included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="QueryParamCollection"/> that contains the headers.
- /// </value>
+ public abstract Uri RequestUri { get; }
public abstract QueryParamCollection Headers { get; }
-
- /// <summary>
- /// Gets the value of the Host header included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Host header.
- /// </value>
- public abstract string Host { get; }
-
- /// <summary>
- /// Gets a value indicating whether the client is authenticated.
- /// </summary>
- /// <value>
- /// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
- /// </value>
- public abstract bool IsAuthenticated { get; }
-
- /// <summary>
- /// Gets a value indicating whether the client connected from the local computer.
- /// </summary>
- /// <value>
- /// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
- /// </value>
- public abstract bool IsLocal { get; }
-
- /// <summary>
- /// Gets a value indicating whether the WebSocket connection is secured.
- /// </summary>
- /// <value>
- /// <c>true</c> if the connection is secured; otherwise, <c>false</c>.
- /// </value>
- public abstract bool IsSecureConnection { get; }
-
- /// <summary>
- /// Gets a value indicating whether the request is a WebSocket connection request.
- /// </summary>
- /// <value>
- /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
- /// </value>
- public abstract bool IsWebSocketRequest { get; }
-
- /// <summary>
- /// Gets the value of the Origin header included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Origin header.
- /// </value>
public abstract string Origin { get; }
-
- /// <summary>
- /// Gets the query string included in the request.
- /// </summary>
- /// <value>
- /// A <see cref="QueryParamCollection"/> that contains the query string parameters.
- /// </value>
- public abstract QueryParamCollection QueryString { get; }
-
- /// <summary>
- /// Gets the URI requested by the client.
- /// </summary>
- /// <value>
- /// A <see cref="Uri"/> that represents the requested URI.
- /// </value>
- public abstract Uri RequestUri { get; }
-
- /// <summary>
- /// Gets the value of the Sec-WebSocket-Key header included in the request.
- /// </summary>
- /// <remarks>
- /// This property provides a part of the information used by the server to prove that it
- /// received a valid WebSocket connection request.
- /// </remarks>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key header.
- /// </value>
- public abstract string SecWebSocketKey { get; }
-
- /// <summary>
- /// Gets the values of the Sec-WebSocket-Protocol header included in the request.
- /// </summary>
- /// <remarks>
- /// This property represents the subprotocols requested by the client.
- /// </remarks>
- /// <value>
- /// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/> instance that provides
- /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol
- /// header.
- /// </value>
public abstract IEnumerable<string> SecWebSocketProtocols { get; }
-
- /// <summary>
- /// Gets the value of the Sec-WebSocket-Version header included in the request.
- /// </summary>
- /// <remarks>
- /// This property represents the WebSocket protocol version.
- /// </remarks>
- /// <value>
- /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Version header.
- /// </value>
public abstract string SecWebSocketVersion { get; }
-
- /// <summary>
- /// Gets the server endpoint as an IP address and a port number.
- /// </summary>
- /// <value>
- /// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
- /// </value>
- public abstract IPEndPoint ServerEndPoint { get; }
-
- /// <summary>
- /// Gets the client information (identity, authentication, and security roles).
- /// </summary>
- /// <value>
- /// A <see cref="IPrincipal"/> that represents the client information.
- /// </value>
+ public abstract string SecWebSocketKey { get; }
+ public abstract CookieCollection CookieCollection { get; }
public abstract IPrincipal User { get; }
-
- /// <summary>
- /// Gets the client endpoint as an IP address and a port number.
- /// </summary>
- /// <value>
- /// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
- /// </value>
- public abstract IPEndPoint UserEndPoint { get; }
-
- /// <summary>
- /// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication
- /// between client and server.
- /// </summary>
- /// <value>
- /// A <see cref="SocketHttpListener.WebSocket"/>.
- /// </value>
+ public abstract bool IsAuthenticated { get; }
+ public abstract bool IsLocal { get; }
+ public abstract bool IsSecureConnection { get; }
public abstract WebSocket WebSocket { get; }
-
- #endregion
}
}
diff --git a/SocketHttpListener/Net/WebSockets/WebSocketValidate.cs b/SocketHttpListener/Net/WebSockets/WebSocketValidate.cs
new file mode 100644
index 000000000..00895ea01
--- /dev/null
+++ b/SocketHttpListener/Net/WebSockets/WebSocketValidate.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MediaBrowser.Model.Net;
+using System.Globalization;
+using WebSocketState = System.Net.WebSockets.WebSocketState;
+
+namespace SocketHttpListener.Net.WebSockets
+{
+ internal static partial class WebSocketValidate
+ {
+ internal const int MaxControlFramePayloadLength = 123;
+ private const int CloseStatusCodeAbort = 1006;
+ private const int CloseStatusCodeFailedTLSHandshake = 1015;
+ private const int InvalidCloseStatusCodesFrom = 0;
+ private const int InvalidCloseStatusCodesTo = 999;
+ private const string Separators = "()<>@,;:\\\"/[]?={} ";
+
+ internal static void ThrowIfInvalidState(WebSocketState currentState, bool isDisposed, WebSocketState[] validStates)
+ {
+ string validStatesText = string.Empty;
+
+ if (validStates != null && validStates.Length > 0)
+ {
+ foreach (WebSocketState validState in validStates)
+ {
+ if (currentState == validState)
+ {
+ // Ordering is important to maintain .NET 4.5 WebSocket implementation exception behavior.
+ if (isDisposed)
+ {
+ throw new ObjectDisposedException(nameof(WebSocket));
+ }
+
+ return;
+ }
+ }
+
+ validStatesText = string.Join(", ", validStates);
+ }
+
+ throw new WebSocketException("net_WebSockets_InvalidState");
+ }
+
+ internal static void ValidateSubprotocol(string subProtocol)
+ {
+ if (string.IsNullOrWhiteSpace(subProtocol))
+ {
+ throw new ArgumentException("net_WebSockets_InvalidEmptySubProtocol");
+ }
+
+ string invalidChar = null;
+ int i = 0;
+ while (i < subProtocol.Length)
+ {
+ char ch = subProtocol[i];
+ if (ch < 0x21 || ch > 0x7e)
+ {
+ invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch);
+ break;
+ }
+
+ if (!char.IsLetterOrDigit(ch) &&
+ Separators.IndexOf(ch) >= 0)
+ {
+ invalidChar = ch.ToString();
+ break;
+ }
+
+ i++;
+ }
+
+ if (invalidChar != null)
+ {
+ throw new ArgumentException("net_WebSockets_InvalidCharInProtocolString");
+ }
+ }
+
+ internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, string statusDescription)
+ {
+ if (closeStatus == WebSocketCloseStatus.Empty && !string.IsNullOrEmpty(statusDescription))
+ {
+ throw new ArgumentException("net_WebSockets_ReasonNotNull");
+ }
+
+ int closeStatusCode = (int)closeStatus;
+
+ if ((closeStatusCode >= InvalidCloseStatusCodesFrom &&
+ closeStatusCode <= InvalidCloseStatusCodesTo) ||
+ closeStatusCode == CloseStatusCodeAbort ||
+ closeStatusCode == CloseStatusCodeFailedTLSHandshake)
+ {
+ // CloseStatus 1006 means Aborted - this will never appear on the wire and is reflected by calling WebSocket.Abort
+ throw new ArgumentException("net_WebSockets_InvalidCloseStatusCode");
+ }
+
+ int length = 0;
+ if (!string.IsNullOrEmpty(statusDescription))
+ {
+ length = Encoding.UTF8.GetByteCount(statusDescription);
+ }
+
+ if (length > MaxControlFramePayloadLength)
+ {
+ throw new ArgumentException("net_WebSockets_InvalidCloseStatusDescription");
+ }
+ }
+
+ internal static void ValidateArraySegment(ArraySegment<byte> arraySegment, string parameterName)
+ {
+ if (arraySegment.Array == null)
+ {
+ throw new ArgumentNullException(parameterName + "." + nameof(arraySegment.Array));
+ }
+ if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length)
+ {
+ throw new ArgumentOutOfRangeException(parameterName + "." + nameof(arraySegment.Offset));
+ }
+ if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset))
+ {
+ throw new ArgumentOutOfRangeException(parameterName + "." + nameof(arraySegment.Count));
+ }
+ }
+
+ internal static void ValidateBuffer(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (offset < 0 || offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+
+ if (count < 0 || count > (buffer.Length - offset))
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener/Primitives/ITextEncoding.cs b/SocketHttpListener/Primitives/ITextEncoding.cs
index 2c25a308c..a256a077d 100644
--- a/SocketHttpListener/Primitives/ITextEncoding.cs
+++ b/SocketHttpListener/Primitives/ITextEncoding.cs
@@ -12,5 +12,10 @@ namespace SocketHttpListener.Primitives
{
return Encoding.UTF8;
}
+
+ public static Encoding GetDefaultEncoding()
+ {
+ return Encoding.UTF8;
+ }
}
}
diff --git a/SocketHttpListener/SocketHttpListener.csproj b/SocketHttpListener/SocketHttpListener.csproj
index 6ed42ea88..da80fa94a 100644
--- a/SocketHttpListener/SocketHttpListener.csproj
+++ b/SocketHttpListener/SocketHttpListener.csproj
@@ -1,120 +1,18 @@
-<?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>{1D74413B-E7CF-455B-B021-F52BDF881542}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>SocketHttpListener</RootNamespace>
- <AssemblyName>SocketHttpListener</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>
+<Project Sdk="Microsoft.NET.Sdk">
+
<ItemGroup>
- <Reference Include="System" />
- <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" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
+
<ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="ByteOrder.cs" />
- <Compile Include="CloseEventArgs.cs" />
- <Compile Include="CloseStatusCode.cs" />
- <Compile Include="CompressionMethod.cs" />
- <Compile Include="ErrorEventArgs.cs" />
- <Compile Include="Ext.cs" />
- <Compile Include="Fin.cs" />
- <Compile Include="HttpBase.cs" />
- <Compile Include="HttpResponse.cs" />
- <Compile Include="Mask.cs" />
- <Compile Include="MessageEventArgs.cs" />
- <Compile Include="Net\AuthenticationSchemeSelector.cs" />
- <Compile Include="Net\BoundaryType.cs" />
- <Compile Include="Net\ChunkedInputStream.cs" />
- <Compile Include="Net\ChunkStream.cs" />
- <Compile Include="Net\CookieHelper.cs" />
- <Compile Include="Net\EndPointListener.cs" />
- <Compile Include="Net\EndPointManager.cs" />
- <Compile Include="Net\EntitySendFormat.cs" />
- <Compile Include="Net\HttpConnection.cs" />
- <Compile Include="Net\HttpListener.cs" />
- <Compile Include="Net\HttpListenerBasicIdentity.cs" />
- <Compile Include="Net\HttpListenerContext.cs" />
- <Compile Include="Net\HttpListenerPrefixCollection.cs" />
- <Compile Include="Net\HttpListenerRequest.cs" />
- <Compile Include="Net\HttpListenerResponse.Managed.cs" />
- <Compile Include="Net\HttpListenerResponse.cs" />
- <Compile Include="Net\HttpRequestStream.cs" />
- <Compile Include="Net\HttpRequestStream.Managed.cs" />
- <Compile Include="Net\HttpResponseStream.cs" />
- <Compile Include="Net\HttpResponseStream.Managed.cs" />
- <Compile Include="Net\HttpStatusCode.cs" />
- <Compile Include="Net\HttpStatusDescription.cs" />
- <Compile Include="Net\HttpStreamAsyncResult.cs" />
- <Compile Include="Net\HttpVersion.cs" />
- <Compile Include="Net\ListenerPrefix.cs" />
- <Compile Include="Net\SocketAcceptor.cs" />
- <Compile Include="Net\UriScheme.cs" />
- <Compile Include="Net\WebHeaderCollection.cs" />
- <Compile Include="Net\WebHeaderEncoding.cs" />
- <Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" />
- <Compile Include="Net\WebSockets\WebSocketContext.cs" />
- <Compile Include="Opcode.cs" />
- <Compile Include="PayloadData.cs" />
- <Compile Include="Primitives\ITextEncoding.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Rsv.cs" />
- <Compile Include="SocketStream.cs" />
- <Compile Include="WebSocket.cs" />
- <Compile Include="WebSocketException.cs" />
- <Compile Include="WebSocketFrame.cs" />
- <Compile Include="WebSocketState.cs" />
+ <Compile Include="..\SharedVersion.cs"/>
</ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </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
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+</Project>
diff --git a/SocketHttpListener/WebSocket.cs b/SocketHttpListener/WebSocket.cs
index 57c075e32..385b25aed 100644
--- a/SocketHttpListener/WebSocket.cs
+++ b/SocketHttpListener/WebSocket.cs
@@ -11,6 +11,8 @@ using MediaBrowser.Model.IO;
using SocketHttpListener.Net.WebSockets;
using SocketHttpListener.Primitives;
using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
+using System.Net.Sockets;
+using WebSocketState = System.Net.WebSockets.WebSocketState;
namespace SocketHttpListener
{
@@ -30,7 +32,6 @@ namespace SocketHttpListener
private CompressionMethod _compression;
private WebSocketContext _context;
private CookieCollection _cookies;
- private string _extensions;
private AutoResetEvent _exitReceiving;
private object _forConn;
private object _forEvent;
@@ -52,9 +53,6 @@ namespace SocketHttpListener
private Stream _stream;
private Uri _uri;
private const string _version = "13";
- private readonly IMemoryStreamFactory _memoryStreamFactory;
-
- private readonly ICryptoProvider _cryptoProvider;
#endregion
@@ -67,43 +65,29 @@ namespace SocketHttpListener
#region Internal Constructors
// As server
- internal WebSocket(HttpListenerWebSocketContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory)
+ internal WebSocket(string protocol)
{
- _context = context;
_protocol = protocol;
- _cryptoProvider = cryptoProvider;
- _memoryStreamFactory = memoryStreamFactory;
+ }
+
+ public void SetContext(HttpListenerWebSocketContext context, Action closeContextFn, Stream stream)
+ {
+ _context = context;
- _closeContext = context.Close;
+ _closeContext = closeContextFn;
_secure = context.IsSecureConnection;
- _stream = context.Stream;
+ _stream = stream;
init();
}
- #endregion
-
- // As server
- internal Func<WebSocketContext, string> CustomHandshakeRequestChecker
+ public static TimeSpan DefaultKeepAliveInterval
{
- get
- {
- return _handshakeRequestChecker ?? (context => null);
- }
-
- set
- {
- _handshakeRequestChecker = value;
- }
+ // In the .NET Framework, this pulls the value from a P/Invoke. Here we just hardcode it to a reasonable default.
+ get { return TimeSpan.FromSeconds(30); }
}
- internal bool IsConnected
- {
- get
- {
- return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing;
- }
- }
+ #endregion
/// <summary>
/// Gets the state of the WebSocket connection.
@@ -146,44 +130,6 @@ namespace SocketHttpListener
#region Private Methods
- // As server
- private bool acceptHandshake()
- {
- var msg = checkIfValidHandshakeRequest(_context);
- if (msg != null)
- {
- error("An error has occurred while connecting: " + msg);
- Close(HttpStatusCode.BadRequest);
-
- return false;
- }
-
- if (_protocol != null &&
- !_context.SecWebSocketProtocols.Contains(protocol => protocol == _protocol))
- _protocol = null;
-
- ////var extensions = _context.Headers["Sec-WebSocket-Extensions"];
- ////if (extensions != null && extensions.Length > 0)
- //// processSecWebSocketExtensionsHeader(extensions);
-
- return sendHttpResponse(createHandshakeResponse());
- }
-
- // As server
- private string checkIfValidHandshakeRequest(WebSocketContext context)
- {
- var headers = context.Headers;
- return context.RequestUri == null
- ? "Invalid request url."
- : !context.IsWebSocketRequest
- ? "Not WebSocket connection request."
- : !validateSecWebSocketKeyHeader(headers["Sec-WebSocket-Key"])
- ? "Invalid Sec-WebSocket-Key header."
- : !validateSecWebSocketVersionClientHeader(headers["Sec-WebSocket-Version"])
- ? "Invalid Sec-WebSocket-Version header."
- : CustomHandshakeRequestChecker(context);
- }
-
private void close(CloseStatusCode code, string reason, bool wait)
{
close(new PayloadData(((ushort)code).Append(reason)), !code.IsReserved(), wait);
@@ -193,20 +139,19 @@ namespace SocketHttpListener
{
lock (_forConn)
{
- if (_readyState == WebSocketState.Closing || _readyState == WebSocketState.Closed)
+ if (_readyState == WebSocketState.CloseSent || _readyState == WebSocketState.Closed)
{
return;
}
- _readyState = WebSocketState.Closing;
+ _readyState = WebSocketState.CloseSent;
}
var e = new CloseEventArgs(payload);
e.WasClean =
closeHandshake(
send ? WebSocketFrame.CreateCloseFrame(Mask.Unmask, payload).ToByteArray() : null,
- wait ? 1000 : 0,
- closeServerResources);
+ wait ? 1000 : 0);
_readyState = WebSocketState.Closed;
try
@@ -219,14 +164,15 @@ namespace SocketHttpListener
}
}
- private bool closeHandshake(byte[] frameAsBytes, int millisecondsTimeout, Action release)
+ private bool closeHandshake(byte[] frameAsBytes, int millisecondsTimeout)
{
var sent = frameAsBytes != null && writeBytes(frameAsBytes);
var received =
millisecondsTimeout == 0 ||
(sent && _exitReceiving != null && _exitReceiving.WaitOne(millisecondsTimeout));
- release();
+ closeServerResources();
+
if (_receivePong != null)
{
_receivePong.Dispose();
@@ -250,7 +196,15 @@ namespace SocketHttpListener
if (_closeContext == null)
return;
- _closeContext();
+ try
+ {
+ _closeContext();
+ }
+ catch (SocketException)
+ {
+ // it could be unable to send the handshake response
+ }
+
_closeContext = null;
_stream = null;
_context = null;
@@ -321,26 +275,6 @@ namespace SocketHttpListener
return res;
}
- // As server
- private HttpResponse createHandshakeResponse()
- {
- var res = HttpResponse.CreateWebSocketResponse();
-
- var headers = res.Headers;
- headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key);
-
- if (_protocol != null)
- headers["Sec-WebSocket-Protocol"] = _protocol;
-
- if (_extensions != null)
- headers["Sec-WebSocket-Extensions"] = _extensions;
-
- if (_cookies.Count > 0)
- res.SetCookies(_cookies);
-
- return res;
- }
-
private MessageEventArgs dequeueFromMessageEventQueue()
{
lock (_forMessageEventQueue)
@@ -403,7 +337,10 @@ namespace SocketHttpListener
{
try
{
- OnOpen.Emit(this, EventArgs.Empty);
+ if (OnOpen != null)
+ {
+ OnOpen(this, EventArgs.Empty);
+ }
}
catch (Exception ex)
{
@@ -463,7 +400,7 @@ namespace SocketHttpListener
private bool processFragments(WebSocketFrame first)
{
- using (var buff = _memoryStreamFactory.CreateNew())
+ using (var buff = new MemoryStream())
{
buff.WriteBytes(first.PayloadData.ApplicationData);
if (!concatenateFragmentsInto(buff))
@@ -691,23 +628,6 @@ namespace SocketHttpListener
receive();
}
- // As server
- private bool validateSecWebSocketKeyHeader(string value)
- {
- if (value == null || value.Length == 0)
- return false;
-
- _base64Key = value;
- return true;
- }
-
- // As server
- private bool validateSecWebSocketVersionClientHeader(string value)
- {
- return true;
- //return value != null && value == _version;
- }
-
private bool writeBytes(byte[] data)
{
try
@@ -715,7 +635,7 @@ namespace SocketHttpListener
_stream.Write(data, 0, data.Length);
return true;
}
- catch (Exception ex)
+ catch (Exception)
{
return false;
}
@@ -728,9 +648,9 @@ namespace SocketHttpListener
// As server
internal void Close(HttpResponse response)
{
- _readyState = WebSocketState.Closing;
-
+ _readyState = WebSocketState.CloseSent;
sendHttpResponse(response);
+
closeServerResources();
_readyState = WebSocketState.Closed;
@@ -747,11 +667,8 @@ namespace SocketHttpListener
{
try
{
- if (acceptHandshake())
- {
- _readyState = WebSocketState.Open;
- open();
- }
+ _readyState = WebSocketState.Open;
+ open();
}
catch (Exception ex)
{
@@ -759,15 +676,6 @@ namespace SocketHttpListener
}
}
- private string CreateResponseKey(string base64Key)
- {
- var buff = new StringBuilder(base64Key, 64);
- buff.Append(_guid);
- var src = _cryptoProvider.ComputeSHA1(Encoding.UTF8.GetBytes(buff.ToString()));
-
- return Convert.ToBase64String(src);
- }
-
#endregion
#region Public Methods
@@ -830,18 +738,20 @@ namespace SocketHttpListener
/// <param name="data">
/// An array of <see cref="byte"/> that represents the binary data to send.
/// </param>
- /// An Action&lt;bool&gt; delegate that references the method(s) called when the send is
- /// complete. A <see cref="bool"/> passed to this delegate is <c>true</c> if the send is
- /// complete successfully; otherwise, <c>false</c>.
public Task SendAsync(byte[] data)
{
- var msg = _readyState.CheckIfOpen() ?? data.CheckIfValidSendData();
+ if (data == null)
+ {
+ throw new ArgumentNullException("data");
+ }
+
+ var msg = _readyState.CheckIfOpen();
if (msg != null)
{
throw new Exception(msg);
}
- return sendAsync(Opcode.Binary, _memoryStreamFactory.CreateNew(data));
+ return sendAsync(Opcode.Binary, new MemoryStream(data));
}
/// <summary>
@@ -853,18 +763,20 @@ namespace SocketHttpListener
/// <param name="data">
/// A <see cref="string"/> that represents the text data to send.
/// </param>
- /// An Action&lt;bool&gt; delegate that references the method(s) called when the send is
- /// complete. A <see cref="bool"/> passed to this delegate is <c>true</c> if the send is
- /// complete successfully; otherwise, <c>false</c>.
public Task SendAsync(string data)
{
- var msg = _readyState.CheckIfOpen() ?? data.CheckIfValidSendData();
+ if (data == null)
+ {
+ throw new ArgumentNullException("data");
+ }
+
+ var msg = _readyState.CheckIfOpen();
if (msg != null)
{
throw new Exception(msg);
}
- return sendAsync(Opcode.Text, _memoryStreamFactory.CreateNew(Encoding.UTF8.GetBytes(data)));
+ return sendAsync(Opcode.Text, new MemoryStream(Encoding.UTF8.GetBytes(data)));
}
#endregion
@@ -880,7 +792,6 @@ namespace SocketHttpListener
void IDisposable.Dispose()
{
Close(CloseStatusCode.Away, null);
- GC.SuppressFinalize(this);
}
#endregion
diff --git a/SocketHttpListener/WebSocketState.cs b/SocketHttpListener/WebSocketState.cs
deleted file mode 100644
index 73b3a49dd..000000000
--- a/SocketHttpListener/WebSocketState.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace SocketHttpListener
-{
- /// <summary>
- /// Contains the values of the state of the WebSocket connection.
- /// </summary>
- /// <remarks>
- /// The values of the state are defined in
- /// <see href="http://www.w3.org/TR/websockets/#dom-websocket-readystate">The WebSocket
- /// API</see>.
- /// </remarks>
- public enum WebSocketState : ushort
- {
- /// <summary>
- /// Equivalent to numeric value 0.
- /// Indicates that the connection has not yet been established.
- /// </summary>
- Connecting = 0,
- /// <summary>
- /// Equivalent to numeric value 1.
- /// Indicates that the connection is established and the communication is possible.
- /// </summary>
- Open = 1,
- /// <summary>
- /// Equivalent to numeric value 2.
- /// Indicates that the connection is going through the closing handshake or
- /// the <c>WebSocket.Close</c> method has been invoked.
- /// </summary>
- Closing = 2,
- /// <summary>
- /// Equivalent to numeric value 3.
- /// Indicates that the connection has been closed or couldn't be opened.
- /// </summary>
- Closed = 3
- }
-}